TF-3005 Apply new design search result list contact view

This commit is contained in:
dab246
2024-10-08 18:37:18 +07:00
committed by Dat H. Pham
parent 4cdd55ed82
commit 6d0c7ed7f5
16 changed files with 461 additions and 277 deletions
@@ -1,15 +1,19 @@
import 'package:core/presentation/utils/keyboard_utils.dart';
import 'dart:async';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.dart';
import 'package:debounce_throttle/debounce_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/autocomplete/auto_complete_pattern.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
@@ -21,6 +25,8 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/get_device_conta
import 'package:tmail_ui_user/features/contact/presentation/model/contact_arguments.dart';
import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_suggestion_box_item.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class ContactController extends BaseController {
@@ -30,20 +36,24 @@ class ContactController extends BaseController {
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
final searchQuery = SearchQuery.initial().obs;
final session = Rxn<Session>();
final listContactSearched = RxList<EmailAddress>();
final searchedContactList = RxList<EmailAddress>();
final selectedContactList = RxList<EmailAddress>();
final contactArguments = Rxn<ContactArguments>();
final searchStatus = SearchStatus.INACTIVE.obs;
final searchViewState = Rx<Either<Failure, Success>>(Right(UIState.idle));
GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor;
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
GetDeviceContactSuggestionsInteractor? _getDeviceContactSuggestionsInteractor;
final Debouncer<String> _deBouncerTime = Debouncer<String>(const Duration(milliseconds: 300), initialValue: '');
final Debouncer<String> _deBouncerTime = Debouncer<String>(
const Duration(milliseconds: 300),
initialValue: ''
);
AccountId? _accountId;
EmailAddress? contactSelected;
SelectedContactCallbackAction? onSelectedContactCallback;
VoidCallback? onDismissContactView;
StreamSubscription<String>? _deBouncerTimeStreamSubscription;
@override
void onInit() {
@@ -51,25 +61,23 @@ class ContactController extends BaseController {
ThemeUtils.setStatusBarTransparentColor();
log('ContactController::onInit():arguments: ${Get.arguments}');
contactArguments.value = Get.arguments;
_deBouncerTime.values.listen((value) {
searchQuery.value = SearchQuery(value);
_searchContactByNameOrEmail(searchQuery.value.value);
});
_deBouncerTimeStreamSubscription = _deBouncerTime.values.listen(_handleDeBounceTimeSearchContact);
textInputSearchFocus.addListener(_onSearchInputTextFocusListener);
}
@override
void onReady() {
super.onReady();
textInputSearchFocus.requestFocus();
if (contactArguments.value != null) {
_accountId = contactArguments.value!.accountId;
session.value = contactArguments.value!.session;
final listContactSelected = contactArguments.value!.listContactSelected;
log('ContactController::onReady(): listContactSelected: $listContactSelected');
if (listContactSelected.isNotEmpty) {
contactSelected = EmailAddress(listContactSelected.first, listContactSelected.first);
selectedContactList.value = contactArguments.value!.selectedContactList
.map((mailAddress) => EmailAddress(null, mailAddress))
.toList();
injectAutoCompleteBindings(contactArguments.value!.session, _accountId);
if (selectedContactList.isEmpty) {
textInputSearchFocus.requestFocus();
}
injectAutoCompleteBindings(session.value, _accountId);
}
if (PlatformInfo.isMobile) {
Future.delayed(
@@ -81,12 +89,21 @@ class ContactController extends BaseController {
@override
void onClose() {
log('ContactController::onClose():');
searchViewState.value = Right(UIState.idle);
textInputSearchFocus.removeListener(_onSearchInputTextFocusListener);
textInputSearchFocus.dispose();
textInputSearchController.dispose();
_deBouncerTimeStreamSubscription?.cancel();
_deBouncerTime.cancel();
super.onClose();
}
void _onSearchInputTextFocusListener() {
if (textInputSearchFocus.hasFocus && textInputSearchController.text.trim().isEmpty) {
searchStatus.value = SearchStatus.INACTIVE;
}
}
void onTextSearchChange(String text) {
_deBouncerTime.value = text;
}
@@ -95,9 +112,23 @@ class ContactController extends BaseController {
_deBouncerTime.value = text;
}
Future<void> _handleDeBounceTimeSearchContact(String value) async {
if (value.trim().isEmpty) {
searchStatus.value = SearchStatus.INACTIVE;
return;
}
searchStatus.value = SearchStatus.ACTIVE;
searchViewState.value = Right(SearchingState());
searchQuery.value = SearchQuery(value);
await _searchContactByNameOrEmail(searchQuery.value.value);
searchViewState.value = Right(UIState.idle);
}
void clearAllTextInputSearchForm() {
textInputSearchController.clear();
searchQuery.value = SearchQuery.initial();
searchedContactList.clear();
textInputSearchFocus.requestFocus();
}
@@ -113,10 +144,10 @@ class ContactController extends BaseController {
}
}
void _searchContactByNameOrEmail(String query) async {
Future<void> _searchContactByNameOrEmail(String query) async {
log('ContactController::_searchContactByNameOrEmail(): query: $query');
final listContact = await _getAutoCompleteSuggestion(query);
listContactSearched.value = listContact;
searchedContactList.value = listContact;
}
Future<List<EmailAddress>> _getAutoCompleteSuggestion(String query) async {
@@ -162,16 +193,46 @@ class ContactController extends BaseController {
}
}
void selectContact(BuildContext context, EmailAddress emailAddress) {
KeyboardUtils.hideKeyboard(context);
popBack(result: emailAddress);
void handleOnSelectContactAction(EmailAddress emailAddress) {
log('ContactController::selectContact:emailAddress = $emailAddress');
final isEmailAddressExist = selectedContactList
.any((contact) => contact.emailAddress == emailAddress.emailAddress);
log('ContactController::selectContact:isEmailAddressExist = $isEmailAddressExist');
if (!isEmailAddressExist) {
selectedContactList.add(emailAddress);
}
}
void handleOnDeleteContactAction(EmailAddress emailAddress) {
log('ContactController::handleOnDeleteContactAction:emailAddress = $emailAddress');
selectedContactList.removeWhere((contact) => contact.emailAddress == emailAddress.emailAddress);
}
void closeContactView() {
textInputSearchController.clear();
searchQuery.value = SearchQuery.initial();
textInputSearchFocus.unfocus();
FocusManager.instance.primaryFocus?.unfocus();
popBack();
}
void handleOnClearFilterAction() {
selectedContactList.clear();
textInputSearchFocus.unfocus();
FocusManager.instance.primaryFocus?.unfocus();
popBack(result: selectedContactList);
}
void handleOnDoneAction() {
textInputSearchFocus.unfocus();
FocusManager.instance.primaryFocus?.unfocus();
popBack(result: selectedContactList);
}
void handleOnSearchBackAction() {
textInputSearchController.clear();
searchQuery.value = SearchQuery.initial();
searchedContactList.clear();
textInputSearchFocus.unfocus();
FocusManager.instance.primaryFocus?.unfocus();
searchStatus.value = SearchStatus.INACTIVE;
}
}
@@ -1,16 +1,14 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
import 'package:tmail_ui_user/features/contact/presentation/contact_controller.dart';
import 'package:tmail_ui_user/features/contact/presentation/styles/contact_view_style.dart';
import 'package:tmail_ui_user/features/contact/presentation/utils/contact_utils.dart';
import 'package:tmail_ui_user/features/contact/presentation/widgets/app_bar_contact_widget.dart';
import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_suggestion_box_item.dart';
import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_item_widget.dart';
import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_list_action_widget.dart';
import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_loading_bar_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -68,10 +66,11 @@ class ContactView extends GetWidget<ContactController> {
hasBackButton: false,
hasSearchButton: true,
searchIconSize: 20,
autoFocus: false,
searchIconColor: AppColor.colorHintSearchBar,
margin: ContactViewStyle.getSearchInputFormMargin(
context,
controller.responsiveUtils
controller.responsiveUtils,
),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
@@ -84,94 +83,22 @@ class ContactView extends GetWidget<ContactController> {
onTextChangeSearchAction: controller.onTextSearchChange,
onSearchTextAction: controller.onSearchTextAction,
)),
if (PlatformInfo.isWeb)
Obx(() {
final username = controller.session.value?.username.value ?? '';
if (username.isNotEmpty) {
final userEmailAddress = EmailAddress(
AppLocalizations.of(context).me,
username);
final fromMeSuggestionEmailAddress = SuggestionEmailAddress(userEmailAddress, state: SuggestionEmailState.valid);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Column(
children: [
ContactSuggestionBoxItem(
fromMeSuggestionEmailAddress,
padding: ContactUtils.getPaddingSearchResultList(context, controller.responsiveUtils),
selectedContactCallbackAction: (contact) {
controller.selectContact(context, contact);
},
),
Padding(
padding: ContactUtils.getPaddingDividerSearchResultList(context, controller.responsiveUtils),
child: const Divider(height: 1, color: AppColor.colorDivider),
),
],
),
);
Obx(() => ContactLoadingBarWidget(
viewState: controller.searchViewState.value,
)),
Expanded(
child: Obx(() {
if (controller.searchStatus.value == SearchStatus.ACTIVE) {
return _buildSearchedContactListView();
} else {
return const SizedBox.shrink();
return _buildSelectedContactListView();
}
}),
Expanded(child: Obx(() {
if (controller.listContactSearched.isNotEmpty) {
if (PlatformInfo.isMobile) {
return Container(
color: Colors.white,
child: ListView.separated(
itemCount: controller.listContactSearched.length,
separatorBuilder: (context, index) {
return Padding(
padding: ContactUtils.getPaddingDividerSearchResultList(context, controller.responsiveUtils),
child: const Divider(height: 1, color: AppColor.colorDivider),
);
},
itemBuilder: (context, index) {
final emailAddress = controller.listContactSearched[index];
final suggestionEmailAddress = _toSuggestionEmailAddress(
emailAddress,
controller.contactSelected != null ? [controller.contactSelected!] : []
);
return ContactSuggestionBoxItem(
suggestionEmailAddress,
padding: ContactUtils.getPaddingSearchResultList(context, controller.responsiveUtils),
selectedContactCallbackAction: (contact) => controller.selectContact(context, contact),
);
}
)
);
} else {
return Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ListView.separated(
itemCount: controller.listContactSearched.length,
separatorBuilder: (context, index) {
return Padding(
padding: ContactUtils.getPaddingDividerSearchResultList(context, controller.responsiveUtils),
child: const Divider(height: 1, color: AppColor.colorDivider),
);
},
itemBuilder: (context, index) {
final emailAddress = controller.listContactSearched[index];
final suggestionEmailAddress = _toSuggestionEmailAddress(
emailAddress,
controller.contactSelected != null ? [controller.contactSelected!] : []
);
return ContactSuggestionBoxItem(
suggestionEmailAddress,
padding: ContactUtils.getPaddingSearchResultList(context, controller.responsiveUtils),
selectedContactCallbackAction: (contact) => controller.selectContact(context, contact),
);
}
)
);
}
} else {
return const SizedBox.shrink();
}
})),
})
),
ContactListActionWidget(
onClearFilterAction: controller.handleOnClearFilterAction,
onDoneAction: controller.handleOnDoneAction,
),
]
),
)
@@ -183,11 +110,65 @@ class ContactView extends GetWidget<ContactController> {
);
}
SuggestionEmailAddress _toSuggestionEmailAddress(EmailAddress item, List<EmailAddress> addedEmailAddresses) {
if (addedEmailAddresses.contains(item)) {
return SuggestionEmailAddress(item, state: SuggestionEmailState.duplicated);
} else {
return SuggestionEmailAddress(item);
}
Widget _buildSearchedContactListView() {
return Obx(() {
if (controller.searchedContactList.isEmpty) {
return const SizedBox.shrink();
}
return ListView.separated(
itemCount: controller.searchedContactList.length,
separatorBuilder: (context, index) {
return Padding(
padding: ContactViewStyle.getDividerSearchResultListPadding(
context,
controller.responsiveUtils
),
child: const Divider(height: 1, color: AppColor.colorDivider),
);
},
itemBuilder: (context, index) {
return ContactItemWidget(
emailAddress: controller.searchedContactList[index],
selectedContactList: controller.selectedContactList,
imagePaths: controller.imagePaths,
responsiveUtils: controller.responsiveUtils,
onSelectContactAction: controller.handleOnSelectContactAction,
onDeleteContactAction: controller.handleOnDeleteContactAction,
);
}
);
});
}
Widget _buildSelectedContactListView() {
return Obx(() {
if (controller.selectedContactList.isEmpty) {
return const SizedBox.shrink();
}
return ListView.separated(
itemCount: controller.selectedContactList.length,
separatorBuilder: (context, index) {
return Padding(
padding: ContactViewStyle.getDividerSearchResultListPadding(
context,
controller.responsiveUtils
),
child: const Divider(height: 1, color: AppColor.colorDivider),
);
},
itemBuilder: (context, index) {
return ContactItemWidget(
emailAddress: controller.selectedContactList[index],
selectedContactList: controller.selectedContactList,
imagePaths: controller.imagePaths,
responsiveUtils: controller.responsiveUtils,
onSelectContactAction: controller.handleOnSelectContactAction,
onDeleteContactAction: controller.handleOnDeleteContactAction,
);
}
);
});
}
}
@@ -6,13 +6,13 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart';
class ContactArguments with EquatableMixin {
final AccountId accountId;
final Session session;
final Set<String> listContactSelected;
final Set<String> selectedContactList;
final String? contactViewTitle;
ContactArguments({
required this.accountId,
required this.session,
required this.listContactSelected,
required this.selectedContactList,
this.contactViewTitle
});
@@ -20,7 +20,7 @@ class ContactArguments with EquatableMixin {
List<Object?> get props => [
accountId,
session,
listContactSelected,
selectedContactList,
contactViewTitle,
];
}
@@ -0,0 +1,30 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
class ContactItemWidgetStyle {
static const TextStyle nameAddressTextStyle = TextStyle(
color: Colors.black,
fontSize: 15,
fontWeight: FontWeight.w600
);
static const TextStyle emailAddressTextStyle = TextStyle(
color: AppColor.colorSubtitle,
fontSize: 13,
fontWeight: FontWeight.w400
);
static EdgeInsetsGeometry getItemPadding(
BuildContext context,
ResponsiveUtils responsiveUtils
) {
if (PlatformInfo.isWeb || responsiveUtils.isScreenWithShortestSide(context)) {
return const EdgeInsets.symmetric(horizontal: 16, vertical: 10);
} else {
return const EdgeInsets.symmetric(horizontal: 32, vertical: 10);
}
}
}
@@ -75,7 +75,7 @@ class ContactViewStyle {
static EdgeInsetsGeometry getSearchInputFormMargin(
BuildContext context,
ResponsiveUtils responsiveUtils
ResponsiveUtils responsiveUtils,
) {
if (PlatformInfo.isWeb || responsiveUtils.isScreenWithShortestSide(context)) {
return const EdgeInsets.symmetric(horizontal: 16, vertical: 10);
@@ -83,4 +83,15 @@ class ContactViewStyle {
return const EdgeInsets.symmetric(horizontal: 32, vertical: 10);
}
}
static EdgeInsets getDividerSearchResultListPadding(
BuildContext context,
ResponsiveUtils responsiveUtils
) {
if (PlatformInfo.isWeb || responsiveUtils.isScreenWithShortestSide(context)) {
return const EdgeInsets.symmetric(horizontal: 16);
} else {
return const EdgeInsets.symmetric(horizontal: 32);
}
}
}
@@ -1,30 +0,0 @@
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
class ContactUtils {
static EdgeInsets getPaddingSearchResultList(BuildContext context, ResponsiveUtils responsiveUtils) {
if (PlatformInfo.isWeb) {
return const EdgeInsets.symmetric(horizontal: 16, vertical: 10);
} else {
if (responsiveUtils.isScreenWithShortestSide(context)) {
return const EdgeInsets.symmetric(horizontal: 16, vertical: 10);
} else {
return const EdgeInsets.symmetric(horizontal: 32, vertical: 10);
}
}
}
static EdgeInsets getPaddingDividerSearchResultList(BuildContext context, ResponsiveUtils responsiveUtils) {
if (PlatformInfo.isWeb) {
return const EdgeInsets.symmetric(horizontal: 16);
} else {
if (responsiveUtils.isScreenWithShortestSide(context)) {
return const EdgeInsets.symmetric(horizontal: 16);
} else {
return const EdgeInsets.symmetric(horizontal: 32);
}
}
}
}
@@ -1,57 +0,0 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/text/input_decoration_builder.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
class ContactInputDecorationBuilder extends InputDecorationBuilder {
@override
InputDecoration build() {
return InputDecoration(
enabledBorder: enabledBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(
width: 0.5,
color: AppColor.colorInputBorderCreateMailbox)),
focusedBorder: enabledBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(
width: 1,
color: AppColor.colorTextButton)),
errorBorder: errorBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(
width: 1,
color: AppColor.colorInputBorderErrorVerifyName)),
focusedErrorBorder: errorBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(
width: 0.5,
color: AppColor.colorInputBorderErrorVerifyName)),
prefixText: prefixText,
labelText: labelText,
floatingLabelBehavior: FloatingLabelBehavior.never,
labelStyle: labelStyle ?? const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w500),
hintText: hintText,
isDense: true,
hintStyle: hintStyle ?? const TextStyle(
color: AppColor.colorEmailAddressFull,
fontWeight: FontWeight.w500,
fontSize: 16),
contentPadding: EdgeInsets.symmetric(
vertical: PlatformInfo.isWeb ? 16 : 12,
horizontal: 12),
errorText: errorText,
errorStyle: errorTextStyle ?? const TextStyle(
color: AppColor.colorInputBorderErrorVerifyName,
fontSize: 13),
filled: true,
fillColor: errorText?.isNotEmpty == true
? AppColor.colorInputBackgroundErrorVerifyName
: AppColor.colorInputBackgroundCreateMailbox);
}
}
@@ -0,0 +1,102 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/avatar/gradient_circle_avatar_icon.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:tmail_ui_user/features/contact/presentation/styles/contact_item_widget_style.dart';
typedef OnSelectContactAction = Function(EmailAddress emailAddress);
typedef OnDeleteContactAction = Function(EmailAddress emailAddress);
class ContactItemWidget extends StatelessWidget {
final EmailAddress emailAddress;
final List<EmailAddress> selectedContactList;
final ImagePaths imagePaths;
final ResponsiveUtils responsiveUtils;
final OnSelectContactAction onSelectContactAction;
final OnDeleteContactAction onDeleteContactAction;
const ContactItemWidget({
Key? key,
required this.emailAddress,
required this.selectedContactList,
required this.imagePaths,
required this.responsiveUtils,
required this.onSelectContactAction,
required this.onDeleteContactAction,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final childWidget = Padding(
padding: ContactItemWidgetStyle.getItemPadding(
context,
responsiveUtils
),
child: Row(
children: [
GradientCircleAvatarIcon(
colors: emailAddress.avatarColors,
label: emailAddress.labelAvatar,
),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
emailAddress.asString(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: ContactItemWidgetStyle.nameAddressTextStyle
),
if (emailAddress.displayName.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
emailAddress.emailAddress,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: ContactItemWidgetStyle.emailAddressTextStyle
)
)
]
)
),
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 12),
child: SvgPicture.asset(
_isSelectedEmailAddress
? imagePaths.icCheckboxSelected
: imagePaths.icCheckboxUnselected,
width: 20,
height: 20,
colorFilter: _isSelectedEmailAddress
? AppColor.primaryColor.asFilter()
: AppColor.colorEmailTileCheckboxUnhover.asFilter(),
),
),
]
),
);
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _isSelectedEmailAddress
? onDeleteContactAction(emailAddress)
: onSelectContactAction(emailAddress),
child: childWidget,
),
);
}
bool get _isSelectedEmailAddress => selectedContactList
.any((contact) => contact.emailAddress == emailAddress.emailAddress);
}
@@ -0,0 +1,66 @@
import 'package:core/presentation/extensions/capitalize_extension.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContactListActionWidget extends StatelessWidget {
final VoidCallback onClearFilterAction;
final VoidCallback onDoneAction;
const ContactListActionWidget({
super.key,
required this.onClearFilterAction,
required this.onDoneAction
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
child: TMailButtonWidget.fromText(
text: AppLocalizations.of(context).clearFilter.capitalizeFirstEach,
backgroundColor: AppColor.colorContactViewClearFilterButton.withOpacity(0.05),
borderRadius: 10,
maxHeight: 44,
minWidth: 156,
maxLines: 1,
textAlign: TextAlign.center,
textStyle: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: AppColor.primaryColor
),
onTapActionCallback: onClearFilterAction,
),
),
const SizedBox(width: 12),
Flexible(
child: TMailButtonWidget.fromText(
text: AppLocalizations.of(context).done,
backgroundColor: AppColor.primaryColor,
borderRadius: 10,
maxHeight: 44,
minWidth: 156,
maxLines: 1,
textAlign: TextAlign.center,
textStyle: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: Colors.white
),
onTapActionCallback: onDoneAction,
),
),
],
),
);
}
}
@@ -0,0 +1,31 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/views/loading/cupertino_loading_widget.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
class ContactLoadingBarWidget extends StatelessWidget {
final Either<Failure, Success> viewState;
const ContactLoadingBarWidget({super.key, required this.viewState});
@override
Widget build(BuildContext context) {
return viewState.fold(
(failure) => const SizedBox.shrink(),
(success) {
if (success is SearchingState) {
return const CupertinoLoadingWidget(
padding: EdgeInsets.symmetric(vertical: 12)
);
} else {
return const SizedBox.shrink();
}
}
);
}
}