TF-3941 Thread Detail Thread level mark read/star, archive and spam

This commit is contained in:
DatDang
2025-08-05 14:15:03 +07:00
committed by Dat H. Pham
parent f44118af48
commit b7cd3f7179
35 changed files with 902 additions and 552 deletions
@@ -7,6 +7,7 @@ import 'package:mockito/mockito.dart';
import 'package:model/email/email_in_thread_status.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
@@ -62,6 +63,8 @@ void main() {
};
when(threadDetailController.emailIdsPresentation)
.thenReturn(initialEmailIdsPresentation.obs);
when(threadDetailController.emailsInThreadDetailInfo)
.thenReturn(<EmailInThreadDetailInfo>[].obs);
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
@@ -11,6 +11,7 @@ import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
@@ -98,6 +99,8 @@ void main() {
.thenReturn(MailboxId(Id('sent-mailbox-id')));
when(threadDetailController.ownEmailAddress)
.thenReturn('9jEYK@example.com');
when(threadDetailController.emailsInThreadDetailInfo)
.thenReturn(<EmailInThreadDetailInfo>[].obs);
// act
threadDetailController.handleRefreshThreadDetailAction(
@@ -8,6 +8,7 @@ import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/list_presentation_email_extensions.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
@@ -85,7 +86,14 @@ void main() {
expect(
streamsConsumed,
emitsInOrder([
Right(PreloadEmailIdsInThreadSuccess([selectedEmail.id!], threadId: ThreadId(Id('1')))),
Right(PreloadEmailIdsInThreadSuccess(
[selectedEmail.id!],
threadId: ThreadId(Id('1')),
emailsInThreadDetailInfo: [selectedEmail].toEmailsInThreadDetailInfo(
sentMailboxId: null,
ownEmailAddress: null,
),
)),
Right(PreloadEmailsByIdsSuccess([selectedEmail])),
]),
);
@@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_detail_presentation_utils.dart';
void main() {
@@ -492,5 +493,248 @@ void main() {
expect(result, isEmpty);
});
});
group('refreshThreadDetailInfo', () {
final sentMailboxId = MailboxId(Id('sent'));
const ownEmailAddress = 'user@example';
test(
'should return empty list when all inputs are empty',
() {
// arrange
final original = <EmailInThreadDetailInfo>[];
final created = <PresentationEmail>[];
final updated = <PresentationEmail>[];
final destroyed = <EmailId>[];
// act
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
original: original,
created: created,
updated: updated,
destroyed: destroyed,
sentMailboxId: sentMailboxId,
ownEmailAddress: ownEmailAddress,
);
// assert
expect(result, isEmpty);
},
);
test(
'should return original list when no changes are made',
() {
// arrange
final id = Id('email-1');
final emailId = EmailId(id);
final original = [
EmailInThreadDetailInfo(
emailId: emailId,
keywords: {},
mailboxIds: {},
isValidToDisplay: true,
),
];
final created = <PresentationEmail>[];
final updated = <PresentationEmail>[];
final destroyed = <EmailId>[];
// act
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
original: original,
created: created,
updated: updated,
destroyed: destroyed,
sentMailboxId: sentMailboxId,
ownEmailAddress: ownEmailAddress,
);
// assert
expect(result, equals(original));
},
);
test(
'should return created emails when no original emails are present',
() {
// arrange
final id = Id('email-1');
final emailId = EmailId(id);
final created = [
PresentationEmail(
id: emailId,
keywords: {},
mailboxIds: {},
),
];
final original = <EmailInThreadDetailInfo>[];
final updated = <PresentationEmail>[];
final destroyed = <EmailId>[];
// act
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
original: original,
created: created,
updated: updated,
destroyed: destroyed,
sentMailboxId: sentMailboxId,
ownEmailAddress: ownEmailAddress,
);
// assert
expect(result, hasLength(1));
expect(result.first.emailId, equals(emailId));
expect(result.first.keywords, equals({}));
expect(result.first.mailboxIds, equals({}));
},
);
test(
'should return updated emails when emails are updated',
() {
// arrange
final id = Id('email-1');
final emailId = EmailId(id);
final original = [
EmailInThreadDetailInfo(
emailId: emailId,
keywords: {},
mailboxIds: {},
isValidToDisplay: true,
),
];
final updated = [
PresentationEmail(
id: emailId,
keywords: {KeyWordIdentifier('test'): true},
mailboxIds: {},
),
];
final created = <PresentationEmail>[];
final destroyed = <EmailId>[];
// act
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
original: original,
created: created,
updated: updated,
destroyed: destroyed,
sentMailboxId: sentMailboxId,
ownEmailAddress: ownEmailAddress,
);
// assert
expect(result, hasLength(1));
expect(result.first.emailId, equals(emailId));
expect(result.first.keywords, equals({KeyWordIdentifier('test'): true}));
expect(result.first.mailboxIds, equals({}));
},
);
test(
'should remove destroyed emails from the result',
() {
// arrange
final id1 = Id('email-1');
final emailId1 = EmailId(id1);
final id2 = Id('email-2');
final emailId2 = EmailId(id2);
final original = [
EmailInThreadDetailInfo(
emailId: emailId1,
keywords: {},
mailboxIds: {},
isValidToDisplay: true,
),
EmailInThreadDetailInfo(
emailId: emailId2,
keywords: {},
mailboxIds: {},
isValidToDisplay: true,
),
];
final created = <PresentationEmail>[];
final updated = <PresentationEmail>[];
final destroyed = [emailId1];
// act
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
original: original,
created: created,
updated: updated,
destroyed: destroyed,
sentMailboxId: sentMailboxId,
ownEmailAddress: ownEmailAddress,
);
// assert
expect(result, hasLength(1));
expect(result.first.emailId, equals(emailId2));
expect(result.first.keywords, equals({}));
expect(result.first.mailboxIds, equals({}));
},
);
test(
'should apply all changes when multiple changes are present',
() {
// arrange
final id1 = Id('email-1');
final emailId1 = EmailId(id1);
final id2 = Id('email-2');
final emailId2 = EmailId(id2);
final id3 = Id('email-3');
final emailId3 = EmailId(id3);
final original = [
EmailInThreadDetailInfo(
emailId: emailId1,
keywords: {},
mailboxIds: {},
isValidToDisplay: true,
),
EmailInThreadDetailInfo(
emailId: emailId2,
keywords: {},
mailboxIds: {},
isValidToDisplay: true,
),
];
final created = [
PresentationEmail(
id: emailId3,
keywords: {KeyWordIdentifier('test'): true},
mailboxIds: {},
),
];
final updated = [
PresentationEmail(
id: emailId1,
keywords: {KeyWordIdentifier('updated'): true},
mailboxIds: {},
),
];
final destroyed = [emailId2];
// act
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
original: original,
created: created,
updated: updated,
destroyed: destroyed,
sentMailboxId: sentMailboxId,
ownEmailAddress: ownEmailAddress,
);
// assert
expect(result, hasLength(2));
expect(result.first.emailId, equals(emailId1));
expect(result.first.keywords, equals({KeyWordIdentifier('updated'): true}));
expect(result.first.mailboxIds, equals({}));
expect(result.last.emailId, equals(emailId3));
expect(result.last.keywords, equals({KeyWordIdentifier('test'): true}));
expect(result.last.mailboxIds, equals({}));
},
);
});
});
}