Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format calendar event description #3351

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:linkify/linkify.dart';
class SanitizeAutolinkFilter {

final HtmlEscape htmlEscape;
final bool escapeHtml;
final _linkifyOption = const LinkifyOptions(
humanize: true,
looseUrl: true,
Expand All @@ -18,7 +19,7 @@ class SanitizeAutolinkFilter {
const UrlLinkifier()
];

SanitizeAutolinkFilter(this.htmlEscape);
SanitizeAutolinkFilter(this.htmlEscape, {this.escapeHtml = true});

String process(String inputText) {
try {
Expand All @@ -36,7 +37,7 @@ class SanitizeAutolinkFilter {

for (var element in elements) {
if (element is TextElement) {
final escapedHtml = htmlEscape.convert(element.text);
final escapedHtml = escapeHtml ? htmlEscape.convert(element.text) : element.text;
htmlTextBuffer.write(escapedHtml);
} else if (element is EmailElement) {
final emailLinkTag = _buildEmailLinkTag(
Expand Down
Original file line number Diff line number Diff line change
@@ -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', ' ');
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@

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/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_maybe_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/html_datasource.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';

class CalendarEventRepositoryImpl extends CalendarEventRepository {

final Map<DataSourceType, CalendarEventDataSource> _calendarEventDataSource;
final HtmlDataSource _htmlDataSource;

CalendarEventRepositoryImpl(this._calendarEventDataSource);
CalendarEventRepositoryImpl(this._calendarEventDataSource, this._htmlDataSource);

@override
Future<List<BlobCalendarEvent>> parse(AccountId accountId, Set<Id> blobIds) {
Expand Down Expand Up @@ -49,4 +54,33 @@ class CalendarEventRepositoryImpl extends CalendarEventRepository {
return _calendarEventDataSource[DataSourceType.network]!
.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,
);
}
}
Original file line number Diff line number Diff line change
@@ -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/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_response.dart';
Expand All @@ -23,4 +24,8 @@ abstract class CalendarEventRepository {
AccountId accountId,
Set<Id> blobIds,
String? language);

Future<List<BlobCalendarEvent>> transformCalendarEventDescription(
List<BlobCalendarEvent> blobCalendarEvents,
TransformConfiguration transformConfiguration);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:core/presentation/state/failure.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:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
Expand All @@ -15,15 +16,19 @@ class ParseCalendarEventInteractor {
Stream<Either<Failure, Success>> execute(
AccountId accountId,
Set<Id> blobIds,
String emailContents
TransformConfiguration transformConfiguration,
) async* {
try {
yield Right(ParseCalendarEventLoading());

final listBlobCalendarEvent = await _calendarEventRepository.parse(accountId, blobIds);
final listBlobCalendarEventWithTransformedDescription = await _calendarEventRepository.transformCalendarEventDescription(
listBlobCalendarEvent,
transformConfiguration,
);

if (listBlobCalendarEvent.isNotEmpty) {
yield Right(ParseCalendarEventSuccess(listBlobCalendarEvent));
if (listBlobCalendarEventWithTransformedDescription.isNotEmpty) {
yield Right(ParseCalendarEventSuccess(listBlobCalendarEventWithTransformedDescription));
} else {
yield Left(ParseCalendarEventFailure(NotFoundCalendarEventException()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import 'package:get/get.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/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/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/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/usecases/calendar_event_accept_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/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';

class CalendarEventInteractorBindings extends InteractorsBindings {

@override
void bindingsDataSource() {
Get.lazyPut<CalendarEventDataSource>(() => Get.find<CalendarEventDataSourceImpl>());
Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>());
}

@override
Expand All @@ -26,6 +31,9 @@ class CalendarEventInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => CalendarEventDataSourceImpl(
Get.find<CalendarEventAPI>(),
Get.find<RemoteExceptionThrower>()));
Get.lazyPut(() => HtmlDataSourceImpl(
Get.find<HtmlAnalyzer>(),
Get.find<CacheExceptionThrower>()));
}

@override
Expand All @@ -44,7 +52,8 @@ class CalendarEventInteractorBindings extends InteractorsBindings {
@override
void bindingsRepositoryImpl() {
Get.lazyPut(() => CalendarEventRepositoryImpl(
{DataSourceType.network: Get.find<CalendarEventDataSource>()}
{DataSourceType.network: Get.find<CalendarEventDataSource>()},
Get.find<HtmlDataSource>(),
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import 'dart:typed_data';

import 'package:better_open_file/better_open_file.dart' as open_file;
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:dio/dio.dart';
import 'package:flutter/cupertino.dart';
Expand Down Expand Up @@ -519,7 +522,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_parseCalendarEventAction(
accountId: mailboxDashBoardController.accountId.value!,
blobIds: success.attachments?.calendarEventBlobIds ?? {},
emailContents: success.htmlEmailContent
);
} else {
emailContents.value = success.htmlEmailContent;
Expand Down Expand Up @@ -564,7 +566,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_parseCalendarEventAction(
accountId: mailboxDashBoardController.accountId.value!,
blobIds: success.attachments?.calendarEventBlobIds ?? {},
emailContents: success.htmlEmailContent
);
} else {
emailContents.value = success.htmlEmailContent;
Expand Down Expand Up @@ -1466,13 +1467,26 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
CapabilityIdentifier.jamesCalendarEvent.isSupported(session, accountId);
}

@visibleForTesting
parseCalendarEventAction({
required AccountId accountId,
required Set<Id> blobIds,
}) => _parseCalendarEventAction(accountId: accountId, blobIds: blobIds);

void _parseCalendarEventAction({
required AccountId accountId,
required Set<Id> blobIds,
required String emailContents
hoangdat marked this conversation as resolved.
Show resolved Hide resolved
}) {
log("SingleEmailController::_parseCalendarEventAction:blobIds: $blobIds");
consumeState(_parseCalendarEventInteractor!.execute(accountId, blobIds, emailContents));
consumeState(_parseCalendarEventInteractor!.execute(
accountId,
blobIds,
TransformConfiguration.fromTextTransformers(const [
SanitizeAutolinkUnescapeHtmlTransformer(),
StandardizeHtmlSanitizingTransformers(),
hoangdat marked this conversation as resolved.
Show resolved Hide resolved
NewLineTransformer(),
])
));
}

void _handleParseCalendarEventSuccess(ParseCalendarEventSuccess success) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@ import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:date_format/date_format.dart' as date_format;
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/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/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/recurrence_rule/recurrence_rule.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';

Expand Down Expand Up @@ -364,4 +375,52 @@ extension CalendarEventExtension on CalendarEvent {
method == EventMethod.request ||
method == EventMethod.add ||
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,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,25 @@ import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_reject_
import 'package:mockito/annotations.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/html_datasource.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 'calendar_event_repository_impl_test.mocks.dart';

@GenerateNiceMocks([MockSpec<CalendarEventDataSource>()])
@GenerateNiceMocks([
MockSpec<CalendarEventDataSource>(),
MockSpec<HtmlDataSource>(),
])
void main() {
final calendarEventNetworkDataSource = MockCalendarEventDataSource();
final htmlDatasource = MockHtmlDataSource();
final calendarEventDataSource = {
DataSourceType.network: calendarEventNetworkDataSource};
final calendarEventRepository = CalendarEventRepositoryImpl(calendarEventDataSource);
final calendarEventRepository = CalendarEventRepositoryImpl(
calendarEventDataSource,
htmlDatasource,
);
final accountId = AccountId(Id('123'));
final blobId = Id('blobId');
const language = 'en';
Expand Down
Loading
Loading