Skip to content

Commit

Permalink
TF-3235 Create seach emails in thread API, datasource and repository
Browse files Browse the repository at this point in the history
  • Loading branch information
tddang-linagora committed Nov 8, 2024
1 parent 12d52fc commit 990f162
Show file tree
Hide file tree
Showing 11 changed files with 605 additions and 5 deletions.
13 changes: 13 additions & 0 deletions lib/features/thread/data/datasource/thread_datasource.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';

abstract class ThreadDataSource {
Future<EmailsResponse> getAllEmail(
Expand All @@ -28,6 +29,18 @@ abstract class ThreadDataSource {
}
);

Future<SearchEmailsResponse> searchEmails(
Session session,
AccountId accountId,
{
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
Filter? filter,
Properties? properties
}
);

Future<EmailChangeResponse> getChanges(
Session session,
AccountId accountId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dar
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';

class LocalThreadDataSourceImpl extends ThreadDataSource {
Expand All @@ -40,6 +41,21 @@ class LocalThreadDataSourceImpl extends ThreadDataSource {
throw UnimplementedError();
}

@override
Future<SearchEmailsResponse> searchEmails(
Session session,
AccountId accountId,
{
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
Filter? filter,
Properties? properties
}
) {
throw UnimplementedError();
}

@override
Future<EmailChangeResponse> getChanges(
Session session,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
import 'package:tmail_ui_user/features/thread/data/network/thread_isolate_worker.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';

class ThreadDataSourceImpl extends ThreadDataSource {
Expand Down Expand Up @@ -56,6 +57,30 @@ class ThreadDataSourceImpl extends ThreadDataSource {
}).catchError(_exceptionThrower.throwException);
}

@override
Future<SearchEmailsResponse> searchEmails(
Session session,
AccountId accountId,
{
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
Filter? filter,
Properties? properties,
}
) {
return Future.sync(() async {
return await threadAPI.searchEmails(
session,
accountId,
limit: limit,
position: position,
sort: sort,
filter: filter,
properties: properties);
}).catchError(_exceptionThrower.throwException);
}

@override
Future<EmailChangeResponse> getChanges(
Session session,
Expand Down
97 changes: 97 additions & 0 deletions lib/features/thread/data/network/thread_api.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'dart:async';

import 'package:core/utils/app_logger.dart';
import 'package:jmap_dart_client/http/http_client.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/request/reference_path.dart';
import 'package:jmap_dart_client/jmap/core/request/request_invocation.dart';
import 'package:jmap_dart_client/jmap/core/response/response_object.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/state.dart';
Expand All @@ -16,8 +19,12 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart';
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet.dart';
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_response.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';

class ThreadAPI {
Expand Down Expand Up @@ -83,6 +90,96 @@ class ThreadAPI {
return EmailsResponse(emailList: resultList?.list, state: resultList?.state);
}

Future<SearchEmailsResponse> searchEmails(
Session session,
AccountId accountId,
{
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
Filter? filter,
Properties? properties
}
) async {
final processingInvocation = ProcessingInvocation();

final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation);

// Email/query
final queryEmailMethod = QueryEmailMethod(accountId);

if (limit != null) queryEmailMethod.addLimit(limit);

if (position != null) queryEmailMethod.addPosition(position);

if (sort != null) queryEmailMethod.addSorts(sort);

if (filter != null) queryEmailMethod.addFilters(filter);

final queryEmailInvocation = jmapRequestBuilder.invocation(queryEmailMethod);

// SearchSnippet/get
final getSearchSnippetMethod = SearchSnippetGetMethod(accountId);
if (filter != null) getSearchSnippetMethod.addFilters(filter);
getSearchSnippetMethod.addReferenceEmailIds(
processingInvocation.createResultReference(
queryEmailInvocation.methodCallId,
ReferencePath.idsPath));
final getSearchSnippetInvocation = jmapRequestBuilder.invocation(
getSearchSnippetMethod);

// Email/get
final getEmailMethod = GetEmailMethod(accountId);

if (properties != null) getEmailMethod.addProperties(properties);

getEmailMethod.addReferenceIds(processingInvocation.createResultReference(
queryEmailInvocation.methodCallId,
ReferencePath.idsPath));

final getEmailInvocation = jmapRequestBuilder.invocation(getEmailMethod);

final capabilities = getEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);

final result = await (jmapRequestBuilder
..usings(capabilities))
.build()
.execute();

final emailResultList = result.parse<GetEmailResponse>(
getEmailInvocation.methodCallId, GetEmailResponse.deserialize);

if (sort != null && emailResultList != null) {
for (var comparator in sort) {
emailResultList.sortEmails(comparator);
}
}

final searchSnippets = _getSearchSnippetsFromResponse(
result,
getSearchSnippetMethodCallId: getSearchSnippetInvocation.methodCallId,
);
return SearchEmailsResponse(
emailList: emailResultList?.list,
state: emailResultList?.state,
searchSnippets: searchSnippets);
}

List<SearchSnippet>? _getSearchSnippetsFromResponse(
ResponseObject response,
{required MethodCallId getSearchSnippetMethodCallId}
) {
try {
return response.parse<SearchSnippetGetResponse>(
getSearchSnippetMethodCallId,
SearchSnippetGetResponse.fromJson)?.list;
} catch (e) {
logError('ThreadAPI::searchEmails:getSearchSnippetsFromResponse: Exception = $e');
return null;
}
}

Future<EmailChangeResponse> getChanges(
Session session,
AccountId accountId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_email.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';

class ThreadRepositoryImpl extends ThreadRepository {
Expand Down Expand Up @@ -287,7 +288,7 @@ class ThreadRepositoryImpl extends ThreadRepository {
}

@override
Future<List<Email>> searchEmails(
Future<List<SearchEmail>> searchEmails(
Session session,
AccountId accountId,
{
Expand All @@ -298,7 +299,7 @@ class ThreadRepositoryImpl extends ThreadRepository {
Properties? properties
}
) async {
final emailResponse = await mapDataSource[DataSourceType.network]!.getAllEmail(
final searchEmailsResponse = await mapDataSource[DataSourceType.network]!.searchEmails(
session,
accountId,
limit: limit,
Expand All @@ -307,7 +308,7 @@ class ThreadRepositoryImpl extends ThreadRepository {
filter: filter,
properties: properties);

return emailResponse.emailList ?? List.empty();
return searchEmailsResponse.toSearchEmails ?? [];
}

@override
Expand Down
2 changes: 1 addition & 1 deletion lib/features/thread/domain/model/email_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class EmailsResponse with EquatableMixin {
final List<Email>? emailList;
final State? state;

EmailsResponse({
const EmailsResponse({
this.emailList,
this.state
});
Expand Down
91 changes: 91 additions & 0 deletions lib/features/thread/domain/model/search_email.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:jmap_dart_client/jmap/mail/email/email.dart';

class SearchEmail extends Email {
final String? searchSnippetSubject;
final String? searchSnippetPreview;

SearchEmail({
super.id,
super.blobId,
super.threadId,
super.mailboxIds,
super.keywords,
super.size,
super.receivedAt,
super.headers,
super.messageId,
super.inReplyTo,
super.references,
super.subject,
super.sentAt,
super.hasAttachment,
super.preview,
super.sender,
super.from,
super.to,
super.cc,
super.bcc,
super.replyTo,
super.textBody,
super.htmlBody,
super.attachments,
super.bodyStructure,
super.bodyValues,
super.headerUserAgent,
super.headerMdn,
super.headerCalendarEvent,
super.sMimeStatusHeader,
super.identityHeader,
required this.searchSnippetSubject,
required this.searchSnippetPreview
});

@override
List<Object?> get props => [
...super.props,
searchSnippetSubject,
searchSnippetPreview,
];

factory SearchEmail.fromEmail(
Email email, {
String? searchSnippetSubject,
String? searchSnippetPreview,
}) {
return SearchEmail(
id: email.id,
blobId: email.blobId,
threadId: email.threadId,
mailboxIds: email.mailboxIds,
keywords: email.keywords,
size: email.size,
receivedAt: email.receivedAt,
headers: email.headers,
messageId: email.messageId,
inReplyTo: email.inReplyTo,
references: email.references,
subject: email.subject,
sentAt: email.sentAt,
hasAttachment: email.hasAttachment,
preview: email.preview,
sender: email.sender,
from: email.from,
to: email.to,
cc: email.cc,
bcc: email.bcc,
replyTo: email.replyTo,
textBody: email.textBody,
htmlBody: email.htmlBody,
attachments: email.attachments,
bodyStructure: email.bodyStructure,
bodyValues: email.bodyValues,
headerUserAgent: email.headerUserAgent,
headerMdn: email.headerMdn,
headerCalendarEvent: email.headerCalendarEvent,
sMimeStatusHeader: email.sMimeStatusHeader,
identityHeader: email.identityHeader,
searchSnippetSubject: searchSnippetSubject,
searchSnippetPreview: searchSnippetPreview,
);
}
}
35 changes: 35 additions & 0 deletions lib/features/thread/domain/model/search_emails_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_email.dart';

class SearchEmailsResponse extends EmailsResponse {
const SearchEmailsResponse({
super.emailList,
super.state,
required this.searchSnippets,
});

final List<SearchSnippet>? searchSnippets;

@override
List<Object?> get props => [...super.props, searchSnippets];

List<SearchEmail>? get toSearchEmails {
if (searchSnippets == null) {
return emailList?.map(SearchEmail.fromEmail).toList();
}

final mapSearchSnippet = Map.fromEntries(
searchSnippets!.map((searchSnippet) => MapEntry(
searchSnippet.emailId,
searchSnippet)));

return emailList?.map((email) {
final searchSnippet = mapSearchSnippet[email.id];
return SearchEmail.fromEmail(
email,
searchSnippetSubject: searchSnippet?.subject,
searchSnippetPreview: searchSnippet?.preview);
}).toList();
}
}
Loading

0 comments on commit 990f162

Please sign in to comment.