TF-4141 Add Action required folder to left menu

This commit is contained in:
dab246
2025-12-16 15:24:38 +07:00
committed by Dat H. Pham
parent 61b8c95123
commit 0e49c18f9d
23 changed files with 641 additions and 13 deletions
@@ -94,4 +94,196 @@ void main() {
expect(ids, ['1']);
});
});
group('ListMailboxNodeExtension::insertAfterStarredOrInbox', () {
late List<MailboxNode> nodes;
MailboxNode buildNode(String id, {String? name, String? role}) {
final mailbox = PresentationMailbox(
MailboxId(Id(id)),
name: name != null ? MailboxName(name) : null,
role: role != null ? Role(role) : null,
);
return MailboxNode(mailbox);
}
test('should insert after Starred when Starred exists (by role)', () {
nodes = [
buildNode('1', name: 'Inbox', role: 'inbox'),
buildNode('2', name: 'Starred', role: 'favorite'),
buildNode('3', name: 'Trash'),
];
final newNode = buildNode('4', name: 'Draft');
nodes.insertAfterStarredOrInbox(newNode);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '2', '4', '3']);
});
test(
'should insert after Starred when role missing but name matches (case-insensitive)',
() {
nodes = [
buildNode('1', name: 'Inbox'),
buildNode('2', name: 'STARRED'), // no role
buildNode('3', name: 'Trash'),
];
final newNode = buildNode('4', name: 'Draft');
nodes.insertAfterStarredOrInbox(newNode);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '2', '4', '3']);
});
test('should insert after Inbox when Starred not found', () {
nodes = [
buildNode('1', name: 'Sent'),
buildNode('2', name: 'Inbox', role: 'inbox'),
buildNode('3', name: 'Trash'),
];
final newNode = buildNode('4', name: 'Archive');
nodes.insertAfterStarredOrInbox(newNode);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '2', '4', '3']);
});
test('should insert at beginning when neither Starred nor Inbox found', () {
nodes = [
buildNode('1', name: 'Sent'),
buildNode('2', name: 'Trash'),
];
final newNode = buildNode('3', name: 'Draft');
nodes.insertAfterStarredOrInbox(newNode);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['3', '1', '2']);
});
test('should not insert if node with same id already exists', () {
nodes = [
buildNode('1', name: 'Inbox', role: 'inbox'),
buildNode('2', name: 'Starred', role: 'favorite'),
];
final newNode =
buildNode('2', name: 'Starred', role: 'favorite'); // duplicate id
nodes.insertAfterStarredOrInbox(newNode);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '2']);
});
});
group('ListMailboxNodeExtension::insertAfterByPriority', () {
late List<MailboxNode> nodes;
MailboxNode buildNode(String id, {String? name, String? role}) {
final mailbox = PresentationMailbox(
MailboxId(Id(id)),
name: name != null ? MailboxName(name) : null,
role: role != null ? Role(role) : null,
);
return MailboxNode(mailbox);
}
bool isInbox(MailboxNode node) =>
node.item.role?.value == PresentationMailbox.inboxRole ||
node.item.name?.name.toLowerCase() == 'inbox';
bool isStarred(MailboxNode node) =>
node.item.role?.value == PresentationMailbox.favoriteRole ||
node.item.name?.name.toLowerCase() == 'starred';
bool isSent(MailboxNode node) =>
node.item.role?.value == PresentationMailbox.sentRole ||
node.item.name?.name.toLowerCase() == 'sent';
test('should insert after first matched priority', () {
nodes = [
buildNode('1', name: 'Inbox', role: 'inbox'),
buildNode('2', name: 'Starred', role: 'favorite'),
buildNode('3', name: 'Sent', role: 'sent'),
];
final newNode = buildNode('4', name: 'Draft');
nodes.insertAfterByPriority(
newNode,
[isStarred, isInbox, isSent],
);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '2', '4', '3']);
});
test('should fallback to second priority when first not found', () {
nodes = [
buildNode('1', name: 'Inbox', role: 'inbox'),
buildNode('2', name: 'Trash'),
];
final newNode = buildNode('3', name: 'Draft');
nodes.insertAfterByPriority(
newNode,
[isStarred, isInbox],
);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '3', '2']);
});
test('should insert at beginning when no priority matched', () {
nodes = [
buildNode('1', name: 'Trash'),
buildNode('2', name: 'Archive'),
];
final newNode = buildNode('3', name: 'Draft');
nodes.insertAfterByPriority(
newNode,
[isStarred, isInbox, isSent],
);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['3', '1', '2']);
});
test('should not insert when node with same id already exists', () {
nodes = [
buildNode('1', name: 'Inbox', role: 'inbox'),
buildNode('2', name: 'Sent', role: 'sent'),
];
final newNode = buildNode('2', name: 'Sent', role: 'sent');
nodes.insertAfterByPriority(
newNode,
[isInbox, isSent],
);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1', '2']);
});
test('should insert into empty list as first element', () {
nodes = [];
final newNode = buildNode('1', name: 'Inbox', role: 'inbox');
nodes.insertAfterByPriority(
newNode,
[isInbox],
);
final ids = nodes.mailboxIds.map((id) => id.id.value).toList();
expect(ids, ['1']);
});
});
}
@@ -57,6 +57,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/rename_mailbox_in
import 'package:tmail_ui_user/features/mailbox/domain/usecases/subaddressing_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_multiple_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/action_required_folder_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
@@ -169,6 +170,7 @@ const fallbackGenerators = {
MockSpec<SubaddressingInteractor>(),
MockSpec<CreateDefaultMailboxInteractor>(),
MockSpec<MoveFolderContentInteractor>(),
MockSpec<ActionRequiredFolderController>(fallbackGenerators: fallbackGenerators),
MockSpec<TreeBuilder>(),
MockSpec<VerifyNameInteractor>(),
MockSpec<GetAllMailboxInteractor>(),
@@ -245,6 +247,7 @@ void main() {
final appGridDashboardController = MockAppGridDashboardController();
final spamReportController = MockSpamReportController();
final networkConnectionController = MockNetworkConnectionController();
final actionRequiredFolderController = MockActionRequiredFolderController();
// mock search controller direct dependencies
final quickSearchEmailInteractor = MockQuickSearchEmailInteractor();
@@ -327,6 +330,7 @@ void main() {
Get.put<AppGridDashboardController>(appGridDashboardController);
Get.put<SpamReportController>(spamReportController);
Get.put<NetworkConnectionController>(networkConnectionController);
Get.put<ActionRequiredFolderController>(actionRequiredFolderController);
Get.put<CachingManager>(cachingManager);
Get.put<LanguageCacheManager>(languageCacheManager);
Get.put<AuthorizationInterceptors>(authorizationInterceptors);
@@ -54,6 +54,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/rename_mailbox_in
import 'package:tmail_ui_user/features/mailbox/domain/usecases/subaddressing_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_multiple_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/action_required_folder_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
@@ -170,6 +171,7 @@ const fallbackGenerators = {
MockSpec<SubaddressingInteractor>(),
MockSpec<CreateDefaultMailboxInteractor>(),
MockSpec<MoveFolderContentInteractor>(),
MockSpec<ActionRequiredFolderController>(fallbackGenerators: fallbackGenerators),
MockSpec<TreeBuilder>(),
MockSpec<VerifyNameInteractor>(),
MockSpec<GetAllMailboxInteractor>(),
@@ -236,6 +238,7 @@ void main() {
final appGridDashboardController = MockAppGridDashboardController();
final spamReportController = MockSpamReportController();
final networkConnectionController = MockNetworkConnectionController();
final actionRequiredFolderController = MockActionRequiredFolderController();
final quickSearchEmailInteractor = MockQuickSearchEmailInteractor();
final saveRecentSearchInteractor = MockSaveRecentSearchInteractor();
@@ -323,6 +326,7 @@ void main() {
Get.put<AppGridDashboardController>(appGridDashboardController);
Get.put<SpamReportController>(spamReportController);
Get.put<NetworkConnectionController>(networkConnectionController);
Get.put<ActionRequiredFolderController>(actionRequiredFolderController);
Get.put<CachingManager>(cachingManager);
Get.put<LanguageCacheManager>(languageCacheManager);
Get.put<AuthorizationInterceptors>(authorizationInterceptors);
@@ -54,6 +54,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/action_required_folder_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
@@ -140,6 +141,7 @@ const fallbackGenerators = {
MockSpec<AppGridDashboardController>(fallbackGenerators: fallbackGenerators),
MockSpec<SpamReportController>(fallbackGenerators: fallbackGenerators),
MockSpec<NetworkConnectionController>(fallbackGenerators: fallbackGenerators),
MockSpec<ActionRequiredFolderController>(fallbackGenerators: fallbackGenerators),
MockSpec<RemoveEmailDraftsInteractor>(),
MockSpec<MoveToMailboxInteractor>(),
MockSpec<MarkAsStarEmailInteractor>(),
@@ -207,6 +209,7 @@ void main() {
final MockAppGridDashboardController appGridDashboardController = MockAppGridDashboardController();
final MockSpamReportController spamReportController = MockSpamReportController();
final MockNetworkConnectionController networkConnectionController = MockNetworkConnectionController();
final MockActionRequiredFolderController actionRequiredFolderController = MockActionRequiredFolderController();
final composerManager = MockComposerManager();
late MailboxDashBoardController mailboxDashboardController;
@@ -353,6 +356,7 @@ void main() {
Get.put<AppGridDashboardController>(appGridDashboardController);
Get.put<SpamReportController>(spamReportController);
Get.put<NetworkConnectionController>(networkConnectionController);
Get.put<ActionRequiredFolderController>(actionRequiredFolderController);
Get.put<GetSessionInteractor>(getSessionInteractor);
Get.put<GetAuthenticatedAccountInteractor>(getAuthenticatedAccountInteractor);
Get.put<UpdateAccountCacheInteractor>(updateAccountCacheInteractor);