TF-35 Add presentation for search mailbox

This commit is contained in:
dab246
2022-02-23 17:10:35 +07:00
committed by Dat H. Pham
parent 690de9483f
commit 9cca03fff3
14 changed files with 206 additions and 72 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0ZM10.4657 4.26022L8 6.727L5.5357 4.26022C5.18423 3.90875 4.61438 3.90875 4.26291 4.26022C3.91144 4.6117 3.91144 5.18154 4.26291 5.53302L6.728 8.001L4.26291 10.47C3.91144 10.8215 3.91144 11.3913 4.26291 11.7428C4.61438 12.0943 5.18423 12.0943 5.5357 11.7428L8 9.275L10.4657 11.7428C10.8172 12.0943 11.387 12.0943 11.7385 11.7428C12.09 11.3913 12.09 10.8215 11.7385 10.47L9.272 8.001L11.7385 5.53302C12.09 5.18154 12.09 4.6117 11.7385 4.26022C11.387 3.90875 10.8172 3.90875 10.4657 4.26022Z" fill="#99A2AD"/>
</svg>

After

Width:  |  Height:  |  Size: 725 B

@@ -80,6 +80,7 @@ class ImagePaths {
String get icFilterMessageAttachments => _getImagePath('ic_filter_message_attachments.svg');
String get icLogoTMailV2 => _getImagePath('logo_tmail_v2.png');
String get icSendToast => _getImagePath('ic_send_toast.svg');
String get icClearTextSearch => _getImagePath('ic_clear_text_search.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
@@ -1,4 +1,5 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
@@ -46,6 +47,8 @@ class MailboxBindings extends Bindings {
Get.lazyPut(() => TreeBuilder());
Get.lazyPut(() => CreateNewMailboxInteractor(Get.find<MailboxRepository>()));
Get.lazyPut(() => Uuid());
Get.lazyPut(() => TextEditingController());
Get.lazyPut(() => FocusNode());
Get.put(MailboxController(
Get.find<GetAllMailboxInteractor>(),
Get.find<DeleteCredentialInteractor>(),
@@ -56,6 +59,8 @@ class MailboxBindings extends Bindings {
Get.find<AppToast>(),
Get.find<ImagePaths>(),
Get.find<ResponsiveUtils>(),
Get.find<TextEditingController>(),
Get.find<FocusNode>(),
Get.find<CachingManager>()));
}
}
@@ -27,8 +27,11 @@ import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbo
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.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/route_navigation.dart';
@@ -47,12 +50,17 @@ class MailboxController extends BaseController {
final AppToast _appToast;
final ImagePaths _imagePaths;
final ResponsiveUtils responsiveUtils;
final TextEditingController searchInputController;
final FocusNode searchFocus;
final CachingManager _cachingManager;
final defaultMailboxList = <PresentationMailbox>[].obs;
final folderMailboxNodeList = <MailboxNode>[].obs;
final searchState = SearchState.initial().obs;
final searchQuery = SearchQuery.initial().obs;
List<PresentationMailbox> allMailboxes = <PresentationMailbox>[];
List<PresentationMailbox> listMailboxSearched = <PresentationMailbox>[];
jmapState.State? currentMailboxState;
@@ -66,6 +74,8 @@ class MailboxController extends BaseController {
this._appToast,
this._imagePaths,
this.responsiveUtils,
this.searchInputController,
this.searchFocus,
this._cachingManager,
);
@@ -101,6 +111,7 @@ class MailboxController extends BaseController {
@override
void onClose() {
mailboxDashBoardController.accountId.close();
searchFocus.dispose();
super.onClose();
}
@@ -142,11 +153,13 @@ class MailboxController extends BaseController {
}
void refreshAllMailbox() {
if (!isSearchActive()) {
final accountId = mailboxDashBoardController.accountId.value;
if (accountId != null) {
consumeState(_getAllMailboxInteractor.execute(accountId));
}
}
}
void refreshMailboxChanges() {
final accountId = mailboxDashBoardController.accountId.value;
@@ -287,7 +300,28 @@ class MailboxController extends BaseController {
}
}
void closeMailboxScreen() {
bool isSearchActive() => searchState.value.searchStatus == SearchStatus.ACTIVE;
void enableSearch() => searchState.value = searchState.value.enableSearchState();
void disableSearch(BuildContext context) {
listMailboxSearched.clear();
searchState.value = searchState.value.disableSearchState();
searchQuery.value = SearchQuery.initial();
searchInputController.clear();
FocusScope.of(context).unfocus();
}
void clearSearchText() {
searchQuery.value = SearchQuery.initial();
searchFocus.requestFocus();
}
void searchMailbox(String value) {
searchQuery.value = SearchQuery(value);
}
void closeMailboxScreen(BuildContext context) {
mailboxDashBoardController.closeDrawer();
}
@@ -8,6 +8,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.d
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_tile_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/search_bar_thread_view_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -33,12 +34,15 @@ class MailboxView extends GetWidget<MailboxController> {
body: Column(
children: [
_buildHeaderMailbox(context),
Obx(() => controller.isSearchActive() ? _buildInputSearchFormWidget(context) : SizedBox.shrink()),
Expanded(child: Container(
color: AppColor.colorBgMailbox,
color: controller.isSearchActive() ? Colors.white : AppColor.colorBgMailbox,
child: RefreshIndicator(
color: AppColor.primaryColor,
onRefresh: () async => controller.refreshAllMailbox(),
child: _buildListMailbox(context)),
child: Obx(() => controller.isSearchActive()
? _buildListMailboxSearched(context, controller.listMailboxSearched)
: _buildListMailbox(context))),
))
]
),
@@ -75,7 +79,7 @@ class MailboxView extends GetWidget<MailboxController> {
padding: EdgeInsets.only(left: 16, right: 16),
child: IconButton(
key: Key('mailbox_close_button'),
onPressed: () => controller.closeMailboxScreen(),
onPressed: () => controller.closeMailboxScreen(context),
icon: SvgPicture.asset(imagePaths.icCloseMailbox, width: 30, height: 30, fit: BoxFit.fill)));
}
@@ -99,12 +103,12 @@ class MailboxView extends GetWidget<MailboxController> {
.build()));
}
Widget _buildSearchFormWidget(BuildContext context) {
Widget _buildSearchBarWidget(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 12),
child: (SearchBarThreadViewWidget(imagePaths)
..hintTextSearch(AppLocalizations.of(context).hint_search_mailboxes)
..addOnOpenSearchViewAction(() {}))
..addOnOpenSearchViewAction(() => controller.enableSearch()))
.build());
}
@@ -132,7 +136,7 @@ class MailboxView extends GetWidget<MailboxController> {
Padding(
padding: EdgeInsets.only(top: 16),
child: Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2)),
_buildSearchFormWidget(context),
_buildSearchBarWidget(context),
_buildLoadingView(),
Obx(() => controller.defaultMailboxList.isNotEmpty
? Container(
@@ -230,5 +234,66 @@ class MailboxView extends GetWidget<MailboxController> {
.toList();
}
// Widget _buildStorageWidget(BuildContext context) => StorageWidgetBuilder(context).build();
Widget _buildInputSearchFormWidget(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 16, bottom: 16),
child: Row(
children: [
_buildBackSearchButton(context),
Expanded(child: (SearchAppBarWidget(
imagePaths,
controller.searchQuery.value,
controller.searchFocus,
controller.searchInputController,
hasBackButton: false,
hasSearchButton: true)
..addPadding(EdgeInsets.zero)
..setMargin(EdgeInsets.only(right: 16))
..addDecoration(BoxDecoration(borderRadius: BorderRadius.circular(12), color: AppColor.colorBgSearchBar))
..addIconClearText(SvgPicture.asset(imagePaths.icClearTextSearch, width: 20, height: 20, fit: BoxFit.fill))
..setHintText(AppLocalizations.of(context).hint_search_mailboxes)
..addOnCancelSearchPressed(() => controller.disableSearch(context))
..addOnClearTextSearchAction(() => controller.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.searchMailbox(query))
..addOnSearchTextAction((query) => controller.searchMailbox(query)))
.build())
]
)
);
}
Widget _buildBackSearchButton(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 5),
child: Material(
borderRadius: BorderRadius.circular(12),
color: Colors.transparent,
child: IconButton(
color: AppColor.colorTextButton,
icon: SvgPicture.asset(
imagePaths.icBack,
width: 20,
height: 20,
color: AppColor.colorTextButton,
fit: BoxFit.fill),
onPressed: () => controller.disableSearch(context)
)
));
}
Widget _buildListMailboxSearched(BuildContext context, List<PresentationMailbox> listMailbox) {
return ListView.builder(
padding: EdgeInsets.all(8),
key: Key('list_mailbox_searched'),
itemCount: listMailbox.length,
shrinkWrap: true,
primary: false,
itemBuilder: (context, index) =>
Obx(() => (MailboxTileBuilder(
imagePaths,
listMailbox[index],
isLastElement: index == listMailbox.length - 1)
..onOpenMailboxAction((mailbox) => controller.selectMailbox(context, mailbox)))
.build()));
}
}
@@ -114,6 +114,7 @@ class MailboxCreatorView extends GetWidget<MailboxCreatorController> {
..autoFocus(true)
..textStyle(TextStyle(color: AppColor.colorNameEmail, fontSize: 16))
..textDecoration((CreateMailboxNameInputDecorationBuilder()
..setHintText(AppLocalizations.of(context).hint_input_create_new_mailbox)
..setErrorText(controller.getErrorInputNameString(context)))
.build()))
.build())
@@ -1,4 +1,5 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart';
@@ -28,6 +28,8 @@ class MailboxDashBoardController extends BaseController {
final AppToast _appToast;
final ImagePaths _imagePaths;
final RemoveEmailDraftsInteractor _removeEmailDraftsInteractor;
final TextEditingController searchInputController;
final FocusNode searchFocus;
final scaffoldKey = GlobalKey<ScaffoldState>();
final selectedMailbox = Rxn<PresentationMailbox>();
@@ -37,8 +39,6 @@ class MailboxDashBoardController extends BaseController {
final searchState = SearchState.initial().obs;
final suggestionSearch = <String>[].obs;
TextEditingController searchInputController = TextEditingController();
FocusNode searchFocus = FocusNode();
SearchQuery? searchQuery;
Session? sessionCurrent;
Map<Role, MailboxId> mapDefaultMailboxId = Map();
@@ -48,6 +48,8 @@ class MailboxDashBoardController extends BaseController {
this._getUserProfileInteractor,
this._appToast,
this._imagePaths,
this.searchInputController,
this.searchFocus,
this._removeEmailDraftsInteractor,
);
@@ -176,14 +178,14 @@ class MailboxDashBoardController extends BaseController {
void disableSearch() {
searchState.value = searchState.value.disableSearchState();
searchQuery = SearchQuery('');
searchQuery = SearchQuery.initial();
clearSuggestionSearch();
searchInputController.clear();
FocusManager.instance.primaryFocus?.unfocus();
}
void clearSearchText() {
searchQuery = SearchQuery('');
searchQuery = SearchQuery.initial();
clearSuggestionSearch();
searchFocus.requestFocus();
}
@@ -200,11 +202,11 @@ class MailboxDashBoardController extends BaseController {
}
}
void searchEmail(String value) {
void searchEmail(BuildContext context, String value) {
searchQuery = SearchQuery(value);
dispatchState(Right(SearchEmailNewQuery(searchQuery ?? SearchQuery(''))));
dispatchState(Right(SearchEmailNewQuery(searchQuery ?? SearchQuery.initial())));
clearSuggestionSearch();
FocusManager.instance.primaryFocus?.unfocus();
FocusScope.of(context).unfocus();
}
void _saveEmailAsDraftsSuccess(SaveEmailAsDraftsSuccess success) {
@@ -6,6 +6,10 @@ class SearchQuery with EquatableMixin {
SearchQuery(this.value);
factory SearchQuery.initial() {
return SearchQuery('');
}
@override
List<Object?> get props => [value];
}
@@ -128,17 +128,18 @@ class ThreadView extends GetWidget<ThreadController> {
Widget _buildSearchForm(BuildContext context) {
return (SearchAppBarWidget(
context,
imagePaths,
controller.searchQuery,
controller.mailboxDashBoardController.suggestionSearch,
controller.mailboxDashBoardController.searchFocus,
controller.mailboxDashBoardController.searchInputController)
controller.mailboxDashBoardController.searchInputController,
suggestionSearch: controller.mailboxDashBoardController.suggestionSearch,)
..addDecoration(BoxDecoration(color: Colors.white))
..setMargin(EdgeInsets.only(right: 10))
..setHintText(AppLocalizations.of(context).search_mail)
..addOnCancelSearchPressed(() => controller.disableSearch())
..addOnClearTextSearchAction(() => controller.mailboxDashBoardController.clearSearchText())
..addOnSuggestionSearchQuery((query) => controller.mailboxDashBoardController.addSuggestionSearch(query))
..addOnSearchTextAction((query) => controller.mailboxDashBoardController.searchEmail(query)))
..addOnTextChangeSearchAction((query) => controller.mailboxDashBoardController.addSuggestionSearch(query))
..addOnSearchTextAction((query) => controller.mailboxDashBoardController.searchEmail(context, query)))
.build();
}
@@ -409,7 +410,8 @@ class ThreadView extends GetWidget<ThreadController> {
context,
imagePaths,
controller.mailboxDashBoardController.suggestionSearch)
..addOnSelectedSuggestion((suggestion) => controller.mailboxDashBoardController.searchEmail(suggestion)))
..addOnSelectedSuggestion((suggestion) =>
controller.mailboxDashBoardController.searchEmail(context, suggestion)))
.build()
: SizedBox.shrink()
);
@@ -3,45 +3,51 @@ import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnCancelSearchPressed = Function();
typedef OnClearTextSearchAction = Function();
typedef OnSuggestionSearchQuery = Function(String);
typedef OnTextChangeSearchAction = Function(String);
typedef OnSearchTextAction = Function(String);
class SearchAppBarWidget {
OnCancelSearchPressed? _onCancelSearchPressed;
OnSuggestionSearchQuery? _onSuggestionSearchQuery;
OnTextChangeSearchAction? _onTextChangeSearchAction;
OnClearTextSearchAction? _onClearTextSearchAction;
OnSearchTextAction? _onSearchTextAction;
final BuildContext _context;
final ImagePaths _imagePaths;
final SearchQuery? _searchQuery;
final TextEditingController? _searchInputController;
final FocusNode? _searchFocusNode;
final List<String>? _suggestionSearch;
final List<String>? suggestionSearch;
final bool hasBackButton;
final bool hasSearchButton;
double? _heightSearchBar;
Decoration? _decoration;
EdgeInsets? _padding;
EdgeInsets? _margin;
String? _hintText;
Widget? _iconClearText;
SearchAppBarWidget(
this._context,
this._imagePaths,
this._searchQuery,
this._suggestionSearch,
this._searchFocusNode,
this._searchInputController,
{
this.hasBackButton = true,
this.suggestionSearch,
this.hasSearchButton = false,
}
);
void addOnCancelSearchPressed(OnCancelSearchPressed onCancelSearchPressed) {
_onCancelSearchPressed = onCancelSearchPressed;
}
void addOnSuggestionSearchQuery(OnSuggestionSearchQuery onSuggestionSearchQuery) {
_onSuggestionSearchQuery = onSuggestionSearchQuery;
void addOnTextChangeSearchAction(OnTextChangeSearchAction onTextChangeSearchAction) {
_onTextChangeSearchAction = onTextChangeSearchAction;
}
void addOnClearTextSearchAction(OnClearTextSearchAction onClearTextSearchAction) {
@@ -64,6 +70,18 @@ class SearchAppBarWidget {
_padding = padding;
}
void setMargin(EdgeInsets margin) {
_margin = margin;
}
void setHintText(String text) {
_hintText = text;
}
void addIconClearText(Widget? icon) {
_iconClearText = icon;
}
Widget build() {
return Container(
key: Key('search_app_bar_widget'),
@@ -71,15 +89,16 @@ class SearchAppBarWidget {
height: _heightSearchBar,
decoration: _decoration,
padding: _padding ?? EdgeInsets.symmetric(vertical: 8),
margin: _margin,
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildBackButton(),
if (hasBackButton) _buildBackButton(),
if (hasSearchButton) _buildSearchButton(),
Expanded(child: _buildSearchInputForm()),
if (_suggestionSearch?.isNotEmpty == true || (_searchQuery != null && _searchQuery!.value.isNotEmpty))
if (suggestionSearch?.isNotEmpty == true || (_searchQuery != null && _searchQuery!.value.isNotEmpty))
_buildClearTextSearchButton(),
]
)
@@ -109,56 +128,47 @@ class SearchAppBarWidget {
return Material(
borderRadius: BorderRadius.circular(12),
color: Colors.transparent,
child: Padding(
padding: EdgeInsets.only(right: 8),
child: IconButton(
color: AppColor.baseTextColor,
icon: SvgPicture.asset(_imagePaths.icComposerClose, color: AppColor.baseTextColor, fit: BoxFit.fill),
padding: EdgeInsets.zero,
icon: _iconClearText ?? SvgPicture.asset(_imagePaths.icComposerClose, fit: BoxFit.fill),
onPressed: () {
_searchInputController?.clear();
if (_onClearTextSearchAction != null) {
_onClearTextSearchAction!();
_onClearTextSearchAction?.call();
}
}
)));
)
);
}
Widget _buildSearchInputForm() {
return (TextFieldBuilder()
..key(Key('search_input_form'))
..textInputAction(TextInputAction.done)
..onChange((value) {
if (_onSuggestionSearchQuery != null) {
_onSuggestionSearchQuery!(value);
}})
..onSubmitted((value) {
if (_onSearchTextAction != null) {
_onSearchTextAction!(value);
}})
..cursorColor(AppColor.baseTextColor)
..onChange((value) => _onTextChangeSearchAction?.call(value))
..onSubmitted((value) => _onSearchTextAction?.call(value))
..cursorColor(AppColor.colorTextButton)
..autoFocus(true)
..addFocusNode(_searchFocusNode)
..textStyle(TextStyle(
color: _searchQuery != null && _searchQuery!.value.isNotEmpty && _suggestionSearch?.isEmpty == true
? AppColor.nameUserColor
: AppColor.baseTextColor,
fontSize: 15,
fontWeight: FontWeight.w500))
..textStyle(TextStyle(color: AppColor.colorNameEmail, fontSize: 17))
..textDecoration(InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 15),
hintText: AppLocalizations.of(_context).search_mail,
hintStyle: TextStyle(
color: AppColor.baseTextColor,
fontWeight: FontWeight.w500,
fontSize: 15.0),
labelStyle: TextStyle(
color: AppColor.baseTextColor,
fontWeight: FontWeight.w500,
fontSize: 15.0)))
contentPadding: EdgeInsets.symmetric(vertical: 7),
hintText: _hintText,
hintStyle: TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0),
labelStyle: TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0)))
..addController(_searchInputController))
.build();
}
Widget _buildSearchButton() {
return Material(
borderRadius: BorderRadius.circular(12),
color: Colors.transparent,
child: IconButton(
icon: SvgPicture.asset(_imagePaths.icSearchBar, width: 18, height: 18, fit: BoxFit.fill),
onPressed: () => _onSearchTextAction?.call(_searchInputController?.text ?? '')
)
);
}
}
@@ -73,7 +73,6 @@ class SearchBarThreadViewWidget {
child: Padding(
padding: EdgeInsets.only(left: 2),
child: IconButton(
color: AppColor.appColor,
icon: SvgPicture.asset(_imagePaths.icSearchBar, width: 18, height: 18, fit: BoxFit.fill),
onPressed: () => _onOpenSearchViewAction?.call()
)
@@ -75,7 +75,7 @@ class SuggestionBoxWidget {
'"${_listData[index]}"',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15, color: AppColor.nameUserColor, fontWeight: FontWeight.w500),
style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail),
))
],
)
@@ -663,4 +663,11 @@ class AppLocalizations {
name: 'discard'
);
}
String get hint_input_create_new_mailbox {
return Intl.message(
'Enter name of mailbox',
name: 'hint_input_create_new_mailbox'
);
}
}