Format calendar event description
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:linkify/linkify.dart';
|
|||||||
class SanitizeAutolinkFilter {
|
class SanitizeAutolinkFilter {
|
||||||
|
|
||||||
final HtmlEscape htmlEscape;
|
final HtmlEscape htmlEscape;
|
||||||
|
final bool escapeHtml;
|
||||||
final _linkifyOption = const LinkifyOptions(
|
final _linkifyOption = const LinkifyOptions(
|
||||||
humanize: true,
|
humanize: true,
|
||||||
looseUrl: true,
|
looseUrl: true,
|
||||||
@@ -18,7 +19,7 @@ class SanitizeAutolinkFilter {
|
|||||||
const UrlLinkifier()
|
const UrlLinkifier()
|
||||||
];
|
];
|
||||||
|
|
||||||
SanitizeAutolinkFilter(this.htmlEscape);
|
SanitizeAutolinkFilter(this.htmlEscape, {this.escapeHtml = true});
|
||||||
|
|
||||||
String process(String inputText) {
|
String process(String inputText) {
|
||||||
try {
|
try {
|
||||||
@@ -36,7 +37,7 @@ class SanitizeAutolinkFilter {
|
|||||||
|
|
||||||
for (var element in elements) {
|
for (var element in elements) {
|
||||||
if (element is TextElement) {
|
if (element is TextElement) {
|
||||||
final escapedHtml = htmlEscape.convert(element.text);
|
final escapedHtml = escapeHtml ? htmlEscape.convert(element.text) : element.text;
|
||||||
htmlTextBuffer.write(escapedHtml);
|
htmlTextBuffer.write(escapedHtml);
|
||||||
} else if (element is EmailElement) {
|
} else if (element is EmailElement) {
|
||||||
final emailLinkTag = _buildEmailLinkTag(
|
final emailLinkTag = _buildEmailLinkTag(
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart';
|
||||||
|
|
||||||
|
class NewLineTransformer extends TextTransformer {
|
||||||
|
const NewLineTransformer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String process(String text, HtmlEscape htmlEscape) {
|
||||||
|
return text
|
||||||
|
.replaceAll('\n', '<br>')
|
||||||
|
.replaceAll('\r', ' ')
|
||||||
|
.replaceAll('\t', ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/sanitize_autolink_filter.dart';
|
||||||
|
|
||||||
|
class SanitizeAutolinkUnescapeHtmlTransformer extends TextTransformer {
|
||||||
|
const SanitizeAutolinkUnescapeHtmlTransformer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String process(String text, HtmlEscape htmlEscape) {
|
||||||
|
return SanitizeAutolinkFilter(htmlEscape, escapeHtml: false).process(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
|
|
||||||
import 'package:core/data/model/source_type/data_source_type.dart';
|
import 'package:core/data/model/source_type/data_source_type.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_response.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_maybe_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_maybe_response.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_reject_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_reject_response.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart';
|
import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_event.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_event.dart';
|
||||||
|
|
||||||
class CalendarEventRepositoryImpl extends CalendarEventRepository {
|
class CalendarEventRepositoryImpl extends CalendarEventRepository {
|
||||||
|
|
||||||
final Map<DataSourceType, CalendarEventDataSource> _calendarEventDataSource;
|
final Map<DataSourceType, CalendarEventDataSource> _calendarEventDataSource;
|
||||||
|
final HtmlDataSource _htmlDataSource;
|
||||||
|
|
||||||
CalendarEventRepositoryImpl(this._calendarEventDataSource);
|
CalendarEventRepositoryImpl(this._calendarEventDataSource, this._htmlDataSource);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<BlobCalendarEvent>> parse(AccountId accountId, Set<Id> blobIds) {
|
Future<List<BlobCalendarEvent>> parse(AccountId accountId, Set<Id> blobIds) {
|
||||||
@@ -49,4 +54,33 @@ class CalendarEventRepositoryImpl extends CalendarEventRepository {
|
|||||||
return _calendarEventDataSource[DataSourceType.network]!
|
return _calendarEventDataSource[DataSourceType.network]!
|
||||||
.rejectEventInvitation(accountId, blobIds, language);
|
.rejectEventInvitation(accountId, blobIds, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<BlobCalendarEvent>> transformCalendarEventDescription(
|
||||||
|
List<BlobCalendarEvent> blobCalendarEvents,
|
||||||
|
TransformConfiguration transformConfiguration,
|
||||||
|
) async {
|
||||||
|
return Future.wait(blobCalendarEvents.map((blobCalendarEvent) async {
|
||||||
|
return BlobCalendarEvent(
|
||||||
|
blobId: blobCalendarEvent.blobId,
|
||||||
|
calendarEventList: await Future.wait(blobCalendarEvent.calendarEventList.map((calendarEvent) {
|
||||||
|
return _transformCalendarEventDescription(calendarEvent, transformConfiguration);
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<CalendarEvent> _transformCalendarEventDescription(
|
||||||
|
CalendarEvent calendarEvent,
|
||||||
|
TransformConfiguration transformConfiguration,
|
||||||
|
) async {
|
||||||
|
return calendarEvent.copyWith(
|
||||||
|
description: calendarEvent.description?.trim().isNotEmpty == true
|
||||||
|
? await _htmlDataSource.transformHtmlEmailContent(
|
||||||
|
calendarEvent.description!,
|
||||||
|
transformConfiguration,
|
||||||
|
)
|
||||||
|
: calendarEvent.description,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_response.dart';
|
||||||
@@ -23,4 +24,8 @@ abstract class CalendarEventRepository {
|
|||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
Set<Id> blobIds,
|
Set<Id> blobIds,
|
||||||
String? language);
|
String? language);
|
||||||
|
|
||||||
|
Future<List<BlobCalendarEvent>> transformCalendarEventDescription(
|
||||||
|
List<BlobCalendarEvent> blobCalendarEvents,
|
||||||
|
TransformConfiguration transformConfiguration);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
@@ -15,15 +16,19 @@ class ParseCalendarEventInteractor {
|
|||||||
Stream<Either<Failure, Success>> execute(
|
Stream<Either<Failure, Success>> execute(
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
Set<Id> blobIds,
|
Set<Id> blobIds,
|
||||||
String emailContents
|
TransformConfiguration transformConfiguration,
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
yield Right(ParseCalendarEventLoading());
|
yield Right(ParseCalendarEventLoading());
|
||||||
|
|
||||||
final listBlobCalendarEvent = await _calendarEventRepository.parse(accountId, blobIds);
|
final listBlobCalendarEvent = await _calendarEventRepository.parse(accountId, blobIds);
|
||||||
|
final listBlobCalendarEventWithTransformedDescription = await _calendarEventRepository.transformCalendarEventDescription(
|
||||||
|
listBlobCalendarEvent,
|
||||||
|
transformConfiguration,
|
||||||
|
);
|
||||||
|
|
||||||
if (listBlobCalendarEvent.isNotEmpty) {
|
if (listBlobCalendarEventWithTransformedDescription.isNotEmpty) {
|
||||||
yield Right(ParseCalendarEventSuccess(listBlobCalendarEvent));
|
yield Right(ParseCalendarEventSuccess(listBlobCalendarEventWithTransformedDescription));
|
||||||
} else {
|
} else {
|
||||||
yield Left(ParseCalendarEventFailure(NotFoundCalendarEventException()));
|
yield Left(ParseCalendarEventFailure(NotFoundCalendarEventException()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import 'package:get/get.dart';
|
|||||||
import 'package:jmap_dart_client/http/http_client.dart';
|
import 'package:jmap_dart_client/http/http_client.dart';
|
||||||
import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
|
import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/datasource_impl/calendar_event_datasource_impl.dart';
|
import 'package:tmail_ui_user/features/email/data/datasource_impl/calendar_event_datasource_impl.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/network/calendar_event_api.dart';
|
import 'package:tmail_ui_user/features/email/data/network/calendar_event_api.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/repository/calendar_event_repository_impl.dart';
|
import 'package:tmail_ui_user/features/email/data/repository/calendar_event_repository_impl.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart';
|
import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart';
|
||||||
@@ -11,6 +14,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_acce
|
|||||||
import 'package:tmail_ui_user/features/email/domain/usecases/maybe_calendar_event_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/maybe_calendar_event_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/parse_calendar_event_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/parse_calendar_event_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
|
|
||||||
class CalendarEventInteractorBindings extends InteractorsBindings {
|
class CalendarEventInteractorBindings extends InteractorsBindings {
|
||||||
@@ -18,6 +22,7 @@ class CalendarEventInteractorBindings extends InteractorsBindings {
|
|||||||
@override
|
@override
|
||||||
void bindingsDataSource() {
|
void bindingsDataSource() {
|
||||||
Get.lazyPut<CalendarEventDataSource>(() => Get.find<CalendarEventDataSourceImpl>());
|
Get.lazyPut<CalendarEventDataSource>(() => Get.find<CalendarEventDataSourceImpl>());
|
||||||
|
Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,6 +31,9 @@ class CalendarEventInteractorBindings extends InteractorsBindings {
|
|||||||
Get.lazyPut(() => CalendarEventDataSourceImpl(
|
Get.lazyPut(() => CalendarEventDataSourceImpl(
|
||||||
Get.find<CalendarEventAPI>(),
|
Get.find<CalendarEventAPI>(),
|
||||||
Get.find<RemoteExceptionThrower>()));
|
Get.find<RemoteExceptionThrower>()));
|
||||||
|
Get.lazyPut(() => HtmlDataSourceImpl(
|
||||||
|
Get.find<HtmlAnalyzer>(),
|
||||||
|
Get.find<CacheExceptionThrower>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -44,7 +52,8 @@ class CalendarEventInteractorBindings extends InteractorsBindings {
|
|||||||
@override
|
@override
|
||||||
void bindingsRepositoryImpl() {
|
void bindingsRepositoryImpl() {
|
||||||
Get.lazyPut(() => CalendarEventRepositoryImpl(
|
Get.lazyPut(() => CalendarEventRepositoryImpl(
|
||||||
{DataSourceType.network: Get.find<CalendarEventDataSource>()}
|
{DataSourceType.network: Get.find<CalendarEventDataSource>()},
|
||||||
|
Get.find<HtmlDataSource>(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,9 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:better_open_file/better_open_file.dart' as open_file;
|
import 'package:better_open_file/better_open_file.dart' as open_file;
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_unescape_html_transformer.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/text/new_line_transformer.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/text/standardize_html_sanitizing_transformers.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
@@ -519,7 +522,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
_parseCalendarEventAction(
|
_parseCalendarEventAction(
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: mailboxDashBoardController.accountId.value!,
|
||||||
blobIds: success.attachments?.calendarEventBlobIds ?? {},
|
blobIds: success.attachments?.calendarEventBlobIds ?? {},
|
||||||
emailContents: success.htmlEmailContent
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emailContents.value = success.htmlEmailContent;
|
emailContents.value = success.htmlEmailContent;
|
||||||
@@ -564,7 +566,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
_parseCalendarEventAction(
|
_parseCalendarEventAction(
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: mailboxDashBoardController.accountId.value!,
|
||||||
blobIds: success.attachments?.calendarEventBlobIds ?? {},
|
blobIds: success.attachments?.calendarEventBlobIds ?? {},
|
||||||
emailContents: success.htmlEmailContent
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emailContents.value = success.htmlEmailContent;
|
emailContents.value = success.htmlEmailContent;
|
||||||
@@ -1466,13 +1467,26 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
CapabilityIdentifier.jamesCalendarEvent.isSupported(session, accountId);
|
CapabilityIdentifier.jamesCalendarEvent.isSupported(session, accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
parseCalendarEventAction({
|
||||||
|
required AccountId accountId,
|
||||||
|
required Set<Id> blobIds,
|
||||||
|
}) => _parseCalendarEventAction(accountId: accountId, blobIds: blobIds);
|
||||||
|
|
||||||
void _parseCalendarEventAction({
|
void _parseCalendarEventAction({
|
||||||
required AccountId accountId,
|
required AccountId accountId,
|
||||||
required Set<Id> blobIds,
|
required Set<Id> blobIds,
|
||||||
required String emailContents
|
|
||||||
}) {
|
}) {
|
||||||
log("SingleEmailController::_parseCalendarEventAction:blobIds: $blobIds");
|
log("SingleEmailController::_parseCalendarEventAction:blobIds: $blobIds");
|
||||||
consumeState(_parseCalendarEventInteractor!.execute(accountId, blobIds, emailContents));
|
consumeState(_parseCalendarEventInteractor!.execute(
|
||||||
|
accountId,
|
||||||
|
blobIds,
|
||||||
|
TransformConfiguration.fromTextTransformers(const [
|
||||||
|
SanitizeAutolinkUnescapeHtmlTransformer(),
|
||||||
|
StandardizeHtmlSanitizingTransformers(),
|
||||||
|
NewLineTransformer(),
|
||||||
|
])
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleParseCalendarEventSuccess(ParseCalendarEventSuccess success) {
|
void _handleParseCalendarEventSuccess(ParseCalendarEventSuccess success) {
|
||||||
|
|||||||
@@ -5,10 +5,21 @@ import 'package:core/presentation/resources/image_paths.dart';
|
|||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:date_format/date_format.dart' as date_format;
|
import 'package:date_format/date_format.dart' as date_format;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/utc_date.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee_participation_status.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee_participation_status.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_duration.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_event_status.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_extension_fields.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_free_busy_status.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_organizer.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_priority.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_privacy.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_sequence.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_method.dart';
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_method.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/calendar/properties/recurrence_rule/recurrence_rule.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/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
|
|
||||||
@@ -364,4 +375,52 @@ extension CalendarEventExtension on CalendarEvent {
|
|||||||
method == EventMethod.request ||
|
method == EventMethod.request ||
|
||||||
method == EventMethod.add ||
|
method == EventMethod.add ||
|
||||||
method == EventMethod.counter;
|
method == EventMethod.counter;
|
||||||
|
|
||||||
|
CalendarEvent copyWith({
|
||||||
|
EventId? eventId,
|
||||||
|
String? title,
|
||||||
|
String? description,
|
||||||
|
DateTime? startDate,
|
||||||
|
DateTime? endDate,
|
||||||
|
UTCDate? startUtcDate,
|
||||||
|
UTCDate? endUtcDate,
|
||||||
|
CalendarDuration? duration,
|
||||||
|
String? timeZone,
|
||||||
|
String? location,
|
||||||
|
EventMethod? method,
|
||||||
|
CalendarSequence? sequence,
|
||||||
|
CalendarPrivacy? privacy,
|
||||||
|
CalendarPriority? priority,
|
||||||
|
CalendarFreeBusyStatus? freeBusyStatus,
|
||||||
|
CalendarEventStatus? status,
|
||||||
|
CalendarOrganizer? organizer,
|
||||||
|
List<CalendarAttendee>? participants,
|
||||||
|
CalendarExtensionFields? extensionFields,
|
||||||
|
List<RecurrenceRule>? recurrenceRules,
|
||||||
|
List<RecurrenceRule>? excludedCalendarEvents,
|
||||||
|
}) {
|
||||||
|
return CalendarEvent(
|
||||||
|
eventId: eventId ?? this.eventId,
|
||||||
|
title: title ?? this.title,
|
||||||
|
description: description ?? this.description,
|
||||||
|
startDate: startDate ?? this.startDate,
|
||||||
|
endDate: endDate ?? this.endDate,
|
||||||
|
startUtcDate: startUtcDate ?? this.startUtcDate,
|
||||||
|
endUtcDate: endUtcDate ?? this.endUtcDate,
|
||||||
|
duration: duration ?? this.duration,
|
||||||
|
timeZone: timeZone ?? this.timeZone,
|
||||||
|
location: location ?? this.location,
|
||||||
|
method: method ?? this.method,
|
||||||
|
sequence: sequence ?? this.sequence,
|
||||||
|
privacy: privacy ?? this.privacy,
|
||||||
|
priority: priority ?? this.priority,
|
||||||
|
freeBusyStatus: freeBusyStatus ?? this.freeBusyStatus,
|
||||||
|
status: status ?? this.status,
|
||||||
|
organizer: organizer ?? this.organizer,
|
||||||
|
participants: participants ?? this.participants,
|
||||||
|
extensionFields: extensionFields ?? this.extensionFields,
|
||||||
|
recurrenceRules: recurrenceRules ?? this.recurrenceRules,
|
||||||
|
excludedCalendarEvents: excludedCalendarEvents ?? this.excludedCalendarEvents,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -9,17 +9,25 @@ import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_reject_
|
|||||||
import 'package:mockito/annotations.dart';
|
import 'package:mockito/annotations.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/repository/calendar_event_repository_impl.dart';
|
import 'package:tmail_ui_user/features/email/data/repository/calendar_event_repository_impl.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_exceptions.dart';
|
import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_exceptions.dart';
|
||||||
|
|
||||||
import 'calendar_event_repository_impl_test.mocks.dart';
|
import 'calendar_event_repository_impl_test.mocks.dart';
|
||||||
|
|
||||||
@GenerateNiceMocks([MockSpec<CalendarEventDataSource>()])
|
@GenerateNiceMocks([
|
||||||
|
MockSpec<CalendarEventDataSource>(),
|
||||||
|
MockSpec<HtmlDataSource>(),
|
||||||
|
])
|
||||||
void main() {
|
void main() {
|
||||||
final calendarEventNetworkDataSource = MockCalendarEventDataSource();
|
final calendarEventNetworkDataSource = MockCalendarEventDataSource();
|
||||||
|
final htmlDatasource = MockHtmlDataSource();
|
||||||
final calendarEventDataSource = {
|
final calendarEventDataSource = {
|
||||||
DataSourceType.network: calendarEventNetworkDataSource};
|
DataSourceType.network: calendarEventNetworkDataSource};
|
||||||
final calendarEventRepository = CalendarEventRepositoryImpl(calendarEventDataSource);
|
final calendarEventRepository = CalendarEventRepositoryImpl(
|
||||||
|
calendarEventDataSource,
|
||||||
|
htmlDatasource,
|
||||||
|
);
|
||||||
final accountId = AccountId(Id('123'));
|
final accountId = AccountId(Id('123'));
|
||||||
final blobId = Id('blobId');
|
final blobId = Id('blobId');
|
||||||
const language = 'en';
|
const language = 'en';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
@@ -18,7 +19,12 @@ import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_
|
|||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:mockito/annotations.dart';
|
import 'package:mockito/annotations.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
|
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/datasource/calendar_event_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/data/repository/calendar_event_repository_impl.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
|
import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_accept_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_accept_state.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
|
||||||
@@ -32,9 +38,11 @@ import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_i
|
|||||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/parse_calendar_event_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/store_event_attendance_status_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/store_event_attendance_status_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart';
|
import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.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/model/blob_calendar_event.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_event.dart';
|
||||||
@@ -46,9 +54,11 @@ import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_
|
|||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
||||||
|
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import '../../../../fixtures/account_fixtures.dart';
|
||||||
import '../../../../fixtures/email_fixtures.dart';
|
import '../../../../fixtures/email_fixtures.dart';
|
||||||
import 'single_email_controller_test.mocks.dart';
|
import 'single_email_controller_test.mocks.dart';
|
||||||
|
|
||||||
@@ -91,6 +101,8 @@ const fallbackGenerators = {
|
|||||||
MockSpec<PrintUtils>(),
|
MockSpec<PrintUtils>(),
|
||||||
MockSpec<ApplicationManager>(),
|
MockSpec<ApplicationManager>(),
|
||||||
MockSpec<ToastManager>(),
|
MockSpec<ToastManager>(),
|
||||||
|
MockSpec<CalendarEventDataSource>(),
|
||||||
|
MockSpec<DioClient>(),
|
||||||
])
|
])
|
||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
@@ -363,4 +375,137 @@ void main() {
|
|||||||
debugDefaultTargetPlatformOverride = null;
|
debugDefaultTargetPlatformOverride = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('_parseCalendarEventAction method test:', () {
|
||||||
|
final calendarEventDataSource = MockCalendarEventDataSource();
|
||||||
|
final calendarEventRepository = CalendarEventRepositoryImpl(
|
||||||
|
{DataSourceType.network :calendarEventDataSource},
|
||||||
|
HtmlDataSourceImpl(
|
||||||
|
HtmlAnalyzer(HtmlTransform(
|
||||||
|
MockDioClient(),
|
||||||
|
const HtmlEscape(),
|
||||||
|
)),
|
||||||
|
CacheExceptionThrower(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
Get.put(ParseCalendarEventInteractor(calendarEventRepository));
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
Get.delete<ParseCalendarEventInteractor>();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should transform all calendar event description url to a tag '
|
||||||
|
'and all new line to <br> tag',
|
||||||
|
() async {
|
||||||
|
// arrange
|
||||||
|
const eventDescription = '\nhttps://example1.com\nhttps://example2.com';
|
||||||
|
const expectedEventDescription = '<html><head></head><body>'
|
||||||
|
'<br>'
|
||||||
|
'<a href="https://example1.com" target="_blank" rel="noreferrer">example1.com</a>'
|
||||||
|
'<br>'
|
||||||
|
'<a href="https://example2.com" target="_blank" rel="noreferrer">example2.com</a>'
|
||||||
|
'</body></html>';
|
||||||
|
final blobId = Id('abc123');
|
||||||
|
final calendarEvent = CalendarEvent(
|
||||||
|
description: eventDescription,
|
||||||
|
);
|
||||||
|
final blobCalendarEvents = [
|
||||||
|
BlobCalendarEvent(
|
||||||
|
blobId: blobId,
|
||||||
|
calendarEventList: [calendarEvent],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
when(calendarEventDataSource.parse(any, any))
|
||||||
|
.thenAnswer((_) async => blobCalendarEvents);
|
||||||
|
|
||||||
|
when(mailboxDashboardController.selectedEmail).thenReturn(Rxn(PresentationEmail()));
|
||||||
|
when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(EmailUIAction()));
|
||||||
|
when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle)));
|
||||||
|
when(mailboxDashboardController.accountId).thenReturn(Rxn(AccountFixtures.aliceAccountId));
|
||||||
|
when(emailSupervisorController.scrollPhysicsPageView).thenReturn(Rxn());
|
||||||
|
|
||||||
|
singleEmailController.onInit();
|
||||||
|
mailboxDashboardController.accountId.refresh();
|
||||||
|
|
||||||
|
// act
|
||||||
|
singleEmailController.parseCalendarEventAction(
|
||||||
|
accountId: AccountFixtures.aliceAccountId,
|
||||||
|
blobIds: {blobId},
|
||||||
|
);
|
||||||
|
await untilCalled(calendarEventDataSource.parse(any, any));
|
||||||
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(
|
||||||
|
singleEmailController.blobCalendarEvent.value,
|
||||||
|
BlobCalendarEvent(
|
||||||
|
blobId: blobId,
|
||||||
|
calendarEventList: [CalendarEvent(description: expectedEventDescription)],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should transform all calendar event description url to a tag '
|
||||||
|
'and all new line to <br> tag '
|
||||||
|
'and remove all xss attempt',
|
||||||
|
() async {
|
||||||
|
// arrange
|
||||||
|
const eventDescription = '\nhttps://example1.com'
|
||||||
|
'\nhttps://example2.com'
|
||||||
|
'\n<script>alert(1)</script>'
|
||||||
|
'\n<a href="javascript:alert(1)">href xss</a>';
|
||||||
|
const expectedEventDescription = '<html><head></head><body>'
|
||||||
|
'<br>'
|
||||||
|
'<a href="https://example1.com" target="_blank" rel="noreferrer">example1.com</a>'
|
||||||
|
'<br>'
|
||||||
|
'<a href="https://example2.com" target="_blank" rel="noreferrer">example2.com</a>'
|
||||||
|
'<br>'
|
||||||
|
'<br>'
|
||||||
|
'<a>href xss</a>'
|
||||||
|
'</body></html>';
|
||||||
|
final blobId = Id('abc123');
|
||||||
|
final calendarEvent = CalendarEvent(
|
||||||
|
description: eventDescription,
|
||||||
|
);
|
||||||
|
final blobCalendarEvents = [
|
||||||
|
BlobCalendarEvent(
|
||||||
|
blobId: blobId,
|
||||||
|
calendarEventList: [calendarEvent],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
when(calendarEventDataSource.parse(any, any))
|
||||||
|
.thenAnswer((_) async => blobCalendarEvents);
|
||||||
|
|
||||||
|
when(mailboxDashboardController.selectedEmail).thenReturn(Rxn(PresentationEmail()));
|
||||||
|
when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(EmailUIAction()));
|
||||||
|
when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle)));
|
||||||
|
when(mailboxDashboardController.accountId).thenReturn(Rxn(AccountFixtures.aliceAccountId));
|
||||||
|
when(emailSupervisorController.scrollPhysicsPageView).thenReturn(Rxn());
|
||||||
|
|
||||||
|
singleEmailController.onInit();
|
||||||
|
mailboxDashboardController.accountId.refresh();
|
||||||
|
|
||||||
|
// act
|
||||||
|
singleEmailController.parseCalendarEventAction(
|
||||||
|
accountId: AccountFixtures.aliceAccountId,
|
||||||
|
blobIds: {blobId},
|
||||||
|
);
|
||||||
|
await untilCalled(calendarEventDataSource.parse(any, any));
|
||||||
|
await Future.delayed(Duration.zero);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(
|
||||||
|
singleEmailController.blobCalendarEvent.value,
|
||||||
|
BlobCalendarEvent(
|
||||||
|
blobId: blobId,
|
||||||
|
calendarEventList: [CalendarEvent(description: expectedEventDescription)],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user