TF-189 Refactor mailbox components: observe only Tree variable

This commit is contained in:
Dat PHAM HOANG
2022-03-11 14:10:51 +07:00
committed by Dat H. Pham
parent af22d2ffaf
commit f2bb1638c0
2 changed files with 67 additions and 123 deletions
@@ -5,9 +5,10 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.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/core/state.dart' as jmapState;
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_mailbox_controller.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/composer/domain/state/save_email_as_drafts_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart';
import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart';
@@ -30,8 +31,6 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_in
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/special_character_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/special_character_validator.dart';
@@ -50,10 +49,9 @@ import 'package:tmail_ui_user/features/thread/presentation/model/search_status.d
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/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmapState;
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class MailboxController extends BaseController { class MailboxController extends BaseMailboxController {
final mailboxDashBoardController = Get.find<MailboxDashBoardController>(); final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final GetAllMailboxInteractor _getAllMailboxInteractor; final GetAllMailboxInteractor _getAllMailboxInteractor;
@@ -64,21 +62,17 @@ class MailboxController extends BaseController {
final DeleteMultipleMailboxInteractor _deleteMultipleMailboxInteractor; final DeleteMultipleMailboxInteractor _deleteMultipleMailboxInteractor;
final VerifyNameInteractor _verifyNameInteractor; final VerifyNameInteractor _verifyNameInteractor;
final RenameMailboxInteractor _renameMailboxInteractor; final RenameMailboxInteractor _renameMailboxInteractor;
final TreeBuilder _treeBuilder;
final Uuid _uuid; final Uuid _uuid;
final AppToast _appToast; final AppToast _appToast;
final ImagePaths _imagePaths; final ImagePaths _imagePaths;
final ResponsiveUtils responsiveUtils; final ResponsiveUtils responsiveUtils;
final CachingManager _cachingManager; final CachingManager _cachingManager;
final defaultMailboxList = <PresentationMailbox>[].obs;
final folderMailboxNodeList = <MailboxNode>[].obs;
final listMailboxSearched = <PresentationMailbox>[].obs; final listMailboxSearched = <PresentationMailbox>[].obs;
final searchState = SearchState.initial().obs; final searchState = SearchState.initial().obs;
final searchQuery = SearchQuery.initial().obs; final searchQuery = SearchQuery.initial().obs;
final currentSelectMode = SelectMode.INACTIVE.obs; final currentSelectMode = SelectMode.INACTIVE.obs;
MailboxTree folderMailboxTree = MailboxTree(MailboxNode.root());
List<PresentationMailbox> allMailboxes = <PresentationMailbox>[]; List<PresentationMailbox> allMailboxes = <PresentationMailbox>[];
TextEditingController searchInputController = TextEditingController(); TextEditingController searchInputController = TextEditingController();
FocusNode searchFocus = FocusNode(); FocusNode searchFocus = FocusNode();
@@ -94,13 +88,13 @@ class MailboxController extends BaseController {
this._deleteMultipleMailboxInteractor, this._deleteMultipleMailboxInteractor,
this._verifyNameInteractor, this._verifyNameInteractor,
this._renameMailboxInteractor, this._renameMailboxInteractor,
this._treeBuilder, treeBuilder,
this._uuid, this._uuid,
this._appToast, this._appToast,
this._imagePaths, this._imagePaths,
this.responsiveUtils, this.responsiveUtils,
this._cachingManager, this._cachingManager,
); ) : super(treeBuilder);
@override @override
void onReady() { void onReady() {
@@ -144,11 +138,11 @@ class MailboxController extends BaseController {
super.onData(newState); super.onData(newState);
newState.map((success) { newState.map((success) {
if (success is GetAllMailboxSuccess) { if (success is GetAllMailboxSuccess) {
allMailboxes = success.defaultMailboxList + success.folderMailboxList; allMailboxes = success.mailboxList;
currentMailboxState = success.currentMailboxState; currentMailboxState = success.currentMailboxState;
defaultMailboxList.value = success.defaultMailboxList; buildTree(allMailboxes);
_setUpMapMailboxIdDefault(success.defaultMailboxList, success.folderMailboxList);
_buildTree(success.folderMailboxList); _setUpMapMailboxIdDefault(allMailboxes, defaultMailboxTree.value, folderMailboxTree.value);
} }
}); });
} }
@@ -208,34 +202,17 @@ class MailboxController extends BaseController {
} }
} }
void _buildTree(List<PresentationMailbox> folderMailboxList) async { void _setUpMapMailboxIdDefault(List<PresentationMailbox> allMailbox, MailboxTree defaultTree, MailboxTree folderTree) {
folderMailboxTree = await _treeBuilder.generateMailboxTree(folderMailboxList);
folderMailboxNodeList.value = folderMailboxTree.root.childrenItems ?? [];
}
void toggleMailboxFolder(MailboxNode mailboxNode) {
final newExpandMode = mailboxNode.expandMode == ExpandMode.COLLAPSE
? ExpandMode.EXPAND
: ExpandMode.COLLAPSE;
final newMailboxNodeList = folderMailboxNodeList.updateNode(
mailboxNode.item.id,
mailboxNode.copyWith(newExpandMode: newExpandMode));
folderMailboxNodeList.value = newMailboxNodeList;
}
void _setUpMapMailboxIdDefault(List<PresentationMailbox> defaultMailboxList, List<PresentationMailbox> folderMailboxList) {
final allMailbox = defaultMailboxList + folderMailboxList;
final mapDefaultMailboxId = Map<Role, MailboxId>.fromIterable( final mapDefaultMailboxId = Map<Role, MailboxId>.fromIterable(
defaultMailboxList, defaultTree.root.childrenItems ?? List<MailboxNode>.empty(),
key: (presentationMailbox) => presentationMailbox.role!, key: (mailboxNode) => mailboxNode.item.role!,
value: (presentationMailbox) => presentationMailbox.id); value: (mailboxNode) => mailboxNode.item.id);
final mapDefaultMailbox = Map<Role, PresentationMailbox>.fromIterable( final mapDefaultMailbox = Map<Role, PresentationMailbox>.fromIterable(
defaultMailboxList, defaultTree.root.childrenItems ?? List<MailboxNode>.empty(),
key: (presentationMailbox) => presentationMailbox.role!, key: (mailboxNode) => mailboxNode.item.role!,
value: (presentationMailbox) => presentationMailbox); value: (mailboxNode) => mailboxNode.item);
final mapMailbox = Map<MailboxId, PresentationMailbox>.fromIterable( final mapMailbox = Map<MailboxId, PresentationMailbox>.fromIterable(
allMailbox, allMailbox,
@@ -381,13 +358,9 @@ class MailboxController extends BaseController {
if (!presentationMailbox.hasParentId()) { if (!presentationMailbox.hasParentId()) {
return presentationMailbox; return presentationMailbox;
} else { } else {
final mailboxNode = folderMailboxTree.findNode(presentationMailbox.id); final mailboxNodePath = findNodePath(presentationMailbox.id);
if (mailboxNode != null) { if (mailboxNodePath != null) {
String mailboxPath = mailboxNode.getPathMailboxNode(folderMailboxTree, defaultMailboxList); return presentationMailbox.toPresentationMailboxWithMailboxPath(mailboxNodePath);
if (mailboxPath.contains('/')) {
mailboxPath = mailboxPath.substring(0, mailboxPath.lastIndexOf('/')).replaceAll('/', ' / ');
}
return presentationMailbox.toPresentationMailboxWithMailboxPath(mailboxPath);
} else { } else {
return presentationMailbox; return presentationMailbox;
} }
@@ -416,34 +389,17 @@ class MailboxController extends BaseController {
? mailbox.toggleSelectPresentationMailbox() ? mailbox.toggleSelectPresentationMailbox()
: mailbox) : mailbox)
.toList(); .toList();
} else {
defaultMailboxList.value = defaultMailboxList
.map((mailbox) => mailbox.id == mailboxSelected.id
? mailbox.toggleSelectPresentationMailbox()
: mailbox)
.toList();
} }
} }
void selectMailboxNode(BuildContext context, MailboxNode mailboxNodeSelected) {
final newMailboxNodeList = folderMailboxNodeList.toggleSelectMailboxNode(mailboxNodeSelected);
folderMailboxNodeList.value = newMailboxNodeList;
}
void _cancelSelectMailbox() { void _cancelSelectMailbox() {
if (isSearchActive()) { if (isSearchActive()) {
listMailboxSearched.value = listMailboxSearched listMailboxSearched.value = listMailboxSearched
.map((mailbox) => mailbox.toSelectedPresentationMailbox(selectMode: SelectMode.INACTIVE)) .map((mailbox) => mailbox.toSelectedPresentationMailbox(selectMode: SelectMode.INACTIVE))
.toList(); .toList();
} else { } else {
defaultMailboxList.value = defaultMailboxList defaultMailboxTree.value.updateNodesUIMode(SelectMode.INACTIVE, ExpandMode.COLLAPSE);
.map((mailbox) => mailbox.toSelectedPresentationMailbox(selectMode: SelectMode.INACTIVE)) folderMailboxTree.value.updateNodesUIMode(SelectMode.INACTIVE, ExpandMode.COLLAPSE);
.toList();
final newMailboxNodeList = folderMailboxNodeList.toSelectMailboxNode(
selectMode: SelectMode.INACTIVE,
newExpandMode: ExpandMode.COLLAPSE);
folderMailboxNodeList.value = newMailboxNodeList;
} }
currentSelectMode.value = SelectMode.INACTIVE; currentSelectMode.value = SelectMode.INACTIVE;
} }
@@ -451,26 +407,19 @@ class MailboxController extends BaseController {
List<PresentationMailbox> get listMailboxSelected { List<PresentationMailbox> get listMailboxSelected {
if (isSearchActive()) { if (isSearchActive()) {
return listMailboxSearched return listMailboxSearched
.where((mailbox) => mailbox.selectMode == SelectMode.ACTIVE) .where((mailbox) => mailbox.selectMode == SelectMode.ACTIVE)
.toList(); .toList();
} else { } else {
final defaultMailboxSelected = defaultMailboxList final defaultMailboxSelected = defaultMailboxTree.value
.where((mailbox) => mailbox.selectMode == SelectMode.ACTIVE) .findNodes((node) => node.selectMode == SelectMode.ACTIVE);
.toList();
final newFolderMailboxTree = MailboxTree(MailboxNode( final folderMailboxSelected = folderMailboxTree.value
MailboxNode.rootItem(), .findNodes((node) => node.selectMode == SelectMode.ACTIVE);
childrenItems: folderMailboxNodeList));
final folderMailboxList = allMailboxes.where((mailbox) => !mailbox.hasRole()).toList();
final folderMailboxSelected = folderMailboxList return [defaultMailboxSelected, folderMailboxSelected]
.where((mailbox) { .expand((node) => node)
final node = newFolderMailboxTree.findNode(mailbox.id); .map((node) => node.item)
return node != null && node.selectMode == SelectMode.ACTIVE; .toList();
})
.toList();
return defaultMailboxSelected + folderMailboxSelected;
} }
} }
@@ -512,20 +461,17 @@ class MailboxController extends BaseController {
} }
void _deleteMailboxAction(PresentationMailbox presentationMailbox) { void _deleteMailboxAction(PresentationMailbox presentationMailbox) {
final newFolderMailboxTree = MailboxTree(MailboxNode( final matchedNode = findMailboxNodeById(presentationMailbox.id);
MailboxNode.rootItem(),
childrenItems: folderMailboxNodeList));
final mailboxNode = newFolderMailboxTree.findNode(presentationMailbox.id);
final accountId = mailboxDashBoardController.accountId.value; final accountId = mailboxDashBoardController.accountId.value;
if (mailboxNode != null && accountId != null) { if (matchedNode != null && accountId != null) {
final allMailboxId = newFolderMailboxTree final descendantIds = matchedNode.descendantsAsList()
.getAllNodes(mailboxNode) .map((node) => node.item.id)
.map((node) => node.item.id) .toList();
.toList();
final allMailboxIdReversed = allMailboxId.reversed.toList(); final descendantIdsReversed = descendantIds.reversed.toList();
consumeState(_deleteMultipleMailboxInteractor.execute(accountId, allMailboxIdReversed)); consumeState(_deleteMultipleMailboxInteractor.execute(accountId, descendantIdsReversed));
} else { } else {
_deleteMailboxFailure(DeleteMultipleMailboxFailure(null)); _deleteMailboxFailure(DeleteMultipleMailboxFailure(null));
} }
@@ -180,13 +180,13 @@ class MailboxView extends GetWidget<MailboxController> {
: _buildLineSpaceUserInformation()), : _buildLineSpaceUserInformation()),
_buildSearchBarWidget(context), _buildSearchBarWidget(context),
_buildLoadingView(), _buildLoadingView(),
Obx(() => controller.defaultMailboxList.isNotEmpty Obx(() => controller.defaultMailboxTree.value.root.childrenItems?.isNotEmpty ?? false
? Container( ? Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
color: Colors.white), color: Colors.white),
margin: EdgeInsets.only(left: 16, right: 16, top: 4), margin: EdgeInsets.only(left: 16, right: 16, top: 4),
child: _buildDefaultMailbox(context, controller.defaultMailboxList)) child: _buildDefaultMailbox(context))
: SizedBox.shrink()), : SizedBox.shrink()),
Padding( Padding(
padding: EdgeInsets.only(left: 25, top: 26, bottom: 12), padding: EdgeInsets.only(left: 25, top: 26, bottom: 12),
@@ -212,26 +212,23 @@ class MailboxView extends GetWidget<MailboxController> {
child: Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2)); child: Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2));
} }
Widget _buildDefaultMailbox(BuildContext context, List<PresentationMailbox> defaultMailbox) { Widget _buildDefaultMailbox(BuildContext context) {
return ListView.builder( return Obx(() => controller.defaultMailboxTree.value.root.childrenItems?.isNotEmpty ?? false
padding: EdgeInsets.all(8), ? Transform(
key: PageStorageKey('default_mailbox_list'), transform: Matrix4.translationValues(-4.0, 0.0, 0.0),
itemCount: defaultMailbox.length, child: Padding(
shrinkWrap: true, padding: EdgeInsets.only(top: 10, bottom: 10),
primary: false, child: TreeView(
itemBuilder: (context, index) => startExpanded: false,
Obx(() => (MailboxTileBuilder( key: Key('default_mailbox_list'),
imagePaths, children: _buildListChildTileWidget(context, controller.defaultMailboxTree.value.root)))
defaultMailbox[index], )
allSelectMode: controller.currentSelectMode.value, : SizedBox.shrink()
isLastElement: index == defaultMailbox.length - 1) );
..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox))
..addOnSelectMailboxActionClick((mailbox) => controller.selectMailbox(context, mailbox)))
.build()));
} }
Widget _buildFolderMailbox(BuildContext context) { Widget _buildFolderMailbox(BuildContext context) {
return Obx(() => controller.folderMailboxNodeList.isNotEmpty return Obx(() => controller.folderMailboxTree.value.root.childrenItems?.isNotEmpty ?? false
? Transform( ? Transform(
transform: Matrix4.translationValues(-4.0, 0.0, 0.0), transform: Matrix4.translationValues(-4.0, 0.0, 0.0),
child: Padding( child: Padding(
@@ -239,15 +236,15 @@ class MailboxView extends GetWidget<MailboxController> {
child: TreeView( child: TreeView(
startExpanded: false, startExpanded: false,
key: Key('folder_mailbox_list'), key: Key('folder_mailbox_list'),
children: _buildListChildTileWidget(context, controller.folderMailboxNodeList))) children: _buildListChildTileWidget(context, controller.folderMailboxTree.value.root)))
) )
: SizedBox.shrink() : SizedBox.shrink()
); );
} }
List<Widget> _buildListChildTileWidget(BuildContext context, List<MailboxNode> listMailboxNode) { List<Widget> _buildListChildTileWidget(BuildContext context, MailboxNode parentNode) {
return listMailboxNode return parentNode.childrenItems
.map((mailboxNode) => mailboxNode.hasChildren() ?.map((mailboxNode) => mailboxNode.hasChildren()
? Padding( ? Padding(
padding: EdgeInsets.only(left: 16), padding: EdgeInsets.only(left: 16),
child: TreeViewChild( child: TreeViewChild(
@@ -261,9 +258,9 @@ class MailboxView extends GetWidget<MailboxController> {
allSelectMode: controller.currentSelectMode.value) allSelectMode: controller.currentSelectMode.value)
..addOnOpenMailboxFolderClick((mailboxNode) => controller.openMailbox(context, mailboxNode.item)) ..addOnOpenMailboxFolderClick((mailboxNode) => controller.openMailbox(context, mailboxNode.item))
..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode)) ..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode))
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxNode(context, mailboxNode))) ..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxNode(mailboxNode)))
.build()), .build()),
children: _buildListChildTileWidget(context, mailboxNode.childrenItems!) children: _buildListChildTileWidget(context, mailboxNode)
).build()) ).build())
: Padding( : Padding(
padding: EdgeInsets.only(left: 16), padding: EdgeInsets.only(left: 16),
@@ -273,10 +270,11 @@ class MailboxView extends GetWidget<MailboxController> {
mailboxNode, mailboxNode,
allSelectMode: controller.currentSelectMode.value) allSelectMode: controller.currentSelectMode.value)
..addOnOpenMailboxFolderClick((mailboxNode) => controller.openMailbox(context, mailboxNode.item)) ..addOnOpenMailboxFolderClick((mailboxNode) => controller.openMailbox(context, mailboxNode.item))
..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxNode(context, mailboxNode))) ..addOnSelectMailboxFolderClick((mailboxNode) => controller.selectMailboxNode(mailboxNode)))
.build(), .build(),
))) )
.toList(); )
).toList() ?? <Widget>[];
} }
Widget _buildInputSearchFormWidget(BuildContext context) { Widget _buildInputSearchFormWidget(BuildContext context) {