Apply new design for recover deleted emails
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -245,6 +245,7 @@ extension AppColor on Color {
|
|||||||
static const blue400 = Color(0xFF80BDFF);
|
static const blue400 = Color(0xFF80BDFF);
|
||||||
static const blue900 = Color(0xFF0F76E7);
|
static const blue900 = Color(0xFF0F76E7);
|
||||||
static const m3Tertiary = Color(0xFF8C9CAF);
|
static const m3Tertiary = Color(0xFF8C9CAF);
|
||||||
|
static const m3Tertiary60 = Color(0xFFD8E1EB);
|
||||||
static const m3Neutral70 = Color(0xFFAEAAAE);
|
static const m3Neutral70 = Color(0xFFAEAAAE);
|
||||||
static const m3Neutral90 = Color(0xFFE6E1E5);
|
static const m3Neutral90 = Color(0xFFE6E1E5);
|
||||||
static const grayBackgroundColor = Color(0xFFF3F6F9);
|
static const grayBackgroundColor = Color(0xFFF3F6F9);
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ class ThemeUtils {
|
|||||||
static const TextStyle textStyleM3BodyLarge = TextStyle(
|
static const TextStyle textStyleM3BodyLarge = TextStyle(
|
||||||
fontFamily: ConstantsUI.fontApp,
|
fontFamily: ConstantsUI.fontApp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 00.15,
|
letterSpacing: -0.15,
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
height: 24 / 17,
|
height: 24 / 17,
|
||||||
color: AppColor.m3SurfaceBackground,
|
color: AppColor.m3SurfaceBackground,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class CustomIconLabeledCheckbox extends LabeledCheckbox {
|
|||||||
required this.focusNode,
|
required this.focusNode,
|
||||||
super.value,
|
super.value,
|
||||||
super.gap = 16.0,
|
super.gap = 16.0,
|
||||||
|
super.padding,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class LabeledCheckbox extends StatelessWidget {
|
|||||||
this.value = false,
|
this.value = false,
|
||||||
this.gap = 4.0,
|
this.gap = 4.0,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
|
this.padding,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
@@ -18,10 +19,11 @@ class LabeledCheckbox extends StatelessWidget {
|
|||||||
final OnChangedAction onChanged;
|
final OnChangedAction onChanged;
|
||||||
final double gap;
|
final double gap;
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
final bodyWidget = InkWell(
|
||||||
onTap: () => onChanged(!(value)),
|
onTap: () => onChanged(!(value)),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -32,6 +34,12 @@ class LabeledCheckbox extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (padding != null) {
|
||||||
|
return Padding(padding: padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget get buildCheckboxWidget => Checkbox(
|
Widget get buildCheckboxWidget => Checkbox(
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
|
||||||
|
|
||||||
|
typedef OnReceiveTimeSelected = void Function(
|
||||||
|
EmailReceiveTimeType receiveTimeType,
|
||||||
|
);
|
||||||
|
|
||||||
|
class DateDropDownButton extends StatelessWidget {
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
final List<EmailReceiveTimeType> receiveTimeTypes;
|
||||||
|
final EmailReceiveTimeType receiveTimeTypeSelected;
|
||||||
|
final DateTime? startDate;
|
||||||
|
final DateTime? endDate;
|
||||||
|
final OnReceiveTimeSelected onReceiveTimeSelected;
|
||||||
|
|
||||||
|
const DateDropDownButton({
|
||||||
|
Key? key,
|
||||||
|
required this.imagePaths,
|
||||||
|
required this.receiveTimeTypes,
|
||||||
|
required this.receiveTimeTypeSelected,
|
||||||
|
required this.onReceiveTimeSelected,
|
||||||
|
this.startDate,
|
||||||
|
this.endDate,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DropdownButtonHideUnderline(
|
||||||
|
child: PointerInterceptor(
|
||||||
|
child: DropdownButton2<EmailReceiveTimeType>(
|
||||||
|
isExpanded: true,
|
||||||
|
items: receiveTimeTypes
|
||||||
|
.map((item) => _buildItemMenu(context, item))
|
||||||
|
.toList(),
|
||||||
|
value: receiveTimeTypeSelected,
|
||||||
|
customButton: Container(
|
||||||
|
height: 40,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColor.m3Neutral90,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsetsDirectional.only(start: 12, end: 8),
|
||||||
|
child: Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
receiveTimeTypeSelected.getTitle(
|
||||||
|
context,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
),
|
||||||
|
style: ThemeUtils.textStyleBodyBody3(
|
||||||
|
color: AppColor.m3SurfaceBackground,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SvgPicture.asset(imagePaths.icDropDown)
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
onReceiveTimeSelected.call(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dropdownStyleData: DropdownStyleData(
|
||||||
|
maxHeight: 332,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(color: AppColor.m3Tertiary60),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(color: Colors.black12, blurRadius: 24)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
elevation: 0,
|
||||||
|
offset: const Offset(0.0, -3.0),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
radius: const Radius.circular(40),
|
||||||
|
thickness: WidgetStateProperty.all<double>(6),
|
||||||
|
thumbVisibility: WidgetStateProperty.all<bool>(true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(
|
||||||
|
height: 44,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownMenuItem<EmailReceiveTimeType> _buildItemMenu(
|
||||||
|
BuildContext context,
|
||||||
|
EmailReceiveTimeType receiveTime,
|
||||||
|
) {
|
||||||
|
return DropdownMenuItem<EmailReceiveTimeType>(
|
||||||
|
value: receiveTime,
|
||||||
|
enabled: receiveTime != receiveTimeTypeSelected,
|
||||||
|
child: PointerInterceptor(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
receiveTime.getTitle(context),
|
||||||
|
style: ThemeUtils.textStyleInter400.copyWith(
|
||||||
|
fontSize: 15,
|
||||||
|
height: 20 / 15,
|
||||||
|
letterSpacing: -0.15,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (receiveTime == receiveTimeTypeSelected)
|
||||||
|
SvgPicture.asset(
|
||||||
|
imagePaths.icChecked,
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.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 DefaultAppBarWidget extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
final ResponsiveUtils responsiveUtils;
|
||||||
|
final VoidCallback onBackAction;
|
||||||
|
|
||||||
|
const DefaultAppBarWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
required this.imagePaths,
|
||||||
|
required this.responsiveUtils,
|
||||||
|
required this.onBackAction,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 64,
|
||||||
|
padding: _getPadding(context),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: ThemeUtils.textStyleM3BodyLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
start: 0,
|
||||||
|
child: TMailButtonWidget.fromIcon(
|
||||||
|
icon: imagePaths.icArrowBack,
|
||||||
|
tooltipMessage: AppLocalizations.of(context).back,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
onTapActionCallback: onBackAction,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeInsetsGeometry _getPadding(BuildContext context) {
|
||||||
|
if (responsiveUtils.isPortraitMobile(context)) {
|
||||||
|
return const EdgeInsetsDirectional.symmetric(horizontal: 16);
|
||||||
|
} else if (responsiveUtils.isLandscapeMobile(context)) {
|
||||||
|
return const EdgeInsetsDirectional.symmetric(horizontal: 24);
|
||||||
|
} else {
|
||||||
|
return const EdgeInsetsDirectional.symmetric(horizontal: 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
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/utils/theme_utils.dart';
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
|
import 'package:model/extensions/email_address_extension.dart';
|
||||||
|
import 'package:model/mailbox/expand_mode.dart';
|
||||||
|
import 'package:super_tag_editor/tag_editor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_tag_item_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_suggestion_item_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||||
|
|
||||||
|
typedef OnSuggestionEmailAddress = Future<List<EmailAddress>> Function(
|
||||||
|
String word, {
|
||||||
|
int? limit,
|
||||||
|
});
|
||||||
|
typedef OnUpdateListEmailAddressAction = void Function(
|
||||||
|
FilterField field,
|
||||||
|
List<EmailAddress> newData,
|
||||||
|
);
|
||||||
|
typedef OnDeleteEmailAddressTypeAction = void Function(FilterField field);
|
||||||
|
typedef OnShowFullListEmailAddressAction = void Function(FilterField field);
|
||||||
|
typedef OnDeleteTagAction = void Function(EmailAddress emailAddress);
|
||||||
|
|
||||||
|
class DefaultAutocompleteInputFieldWidget extends StatefulWidget {
|
||||||
|
final FilterField field;
|
||||||
|
final List<EmailAddress> listEmailAddress;
|
||||||
|
final ExpandMode expandMode;
|
||||||
|
final TextEditingController? controller;
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
final GlobalKey? keyTagEditor;
|
||||||
|
final FocusNode? nextFocusNode;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
final int minInputLengthAutocomplete;
|
||||||
|
final OnSuggestionEmailAddress? onSuggestionEmailAddress;
|
||||||
|
final OnDeleteTagAction? onDeleteTag;
|
||||||
|
final OnUpdateListEmailAddressAction? onUpdateListEmailAddressAction;
|
||||||
|
final OnDeleteEmailAddressTypeAction? onDeleteEmailAddressTypeAction;
|
||||||
|
final OnShowFullListEmailAddressAction? onShowFullListEmailAddressAction;
|
||||||
|
final OnRemoveDraggableEmailAddressAction?
|
||||||
|
onRemoveDraggableEmailAddressAction;
|
||||||
|
final VoidCallback? onSearchAction;
|
||||||
|
|
||||||
|
const DefaultAutocompleteInputFieldWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.field,
|
||||||
|
required this.listEmailAddress,
|
||||||
|
this.expandMode = ExpandMode.EXPAND,
|
||||||
|
this.minInputLengthAutocomplete =
|
||||||
|
AppConfig.defaultMinInputLengthAutocomplete,
|
||||||
|
this.focusNode,
|
||||||
|
this.keyTagEditor,
|
||||||
|
this.nextFocusNode,
|
||||||
|
this.padding,
|
||||||
|
this.onSuggestionEmailAddress,
|
||||||
|
this.onDeleteTag,
|
||||||
|
this.onUpdateListEmailAddressAction,
|
||||||
|
this.onDeleteEmailAddressTypeAction,
|
||||||
|
this.onShowFullListEmailAddressAction,
|
||||||
|
this.onRemoveDraggableEmailAddressAction,
|
||||||
|
this.controller,
|
||||||
|
this.onSearchAction,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DefaultAutocompleteInputFieldWidget> createState() =>
|
||||||
|
_DefaultAutocompleteInputFieldWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DefaultAutocompleteInputFieldWidgetState
|
||||||
|
extends State<DefaultAutocompleteInputFieldWidget> {
|
||||||
|
final _imagePaths = Get.find<ImagePaths>();
|
||||||
|
|
||||||
|
bool _lastTagFocused = false;
|
||||||
|
bool _isDragging = false;
|
||||||
|
late List<EmailAddress> _currentListEmailAddress;
|
||||||
|
Timer? _gapBetweenTagChangedAndFindSuggestion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_currentListEmailAddress = widget.listEmailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(
|
||||||
|
covariant DefaultAutocompleteInputFieldWidget oldWidget,
|
||||||
|
) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.listEmailAddress != oldWidget.listEmailAddress) {
|
||||||
|
_currentListEmailAddress = widget.listEmailAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bodyWidget = LayoutBuilder(builder: ((_, constraints) {
|
||||||
|
final tagEditor = TagEditor<SuggestionEmailAddress>(
|
||||||
|
key: widget.keyTagEditor,
|
||||||
|
length: _collapsedListEmailAddress.length,
|
||||||
|
controller: widget.controller,
|
||||||
|
focusNodeKeyboard: widget.focusNode,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
cursorColor: AppColor.primaryColor,
|
||||||
|
debounceDuration: const Duration(milliseconds: 150),
|
||||||
|
inputDecoration: InputDecoration(
|
||||||
|
filled: true,
|
||||||
|
constraints: const BoxConstraints(maxHeight: 40),
|
||||||
|
fillColor: Colors.white,
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
hintText: widget.field.getHintText(AppLocalizations.of(context)),
|
||||||
|
hintStyle: ThemeUtils.textStyleBodyBody3(
|
||||||
|
color: AppColor.m3Tertiary,
|
||||||
|
),
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: _currentListEmailAddress.isNotEmpty
|
||||||
|
? const EdgeInsetsDirectional.symmetric(vertical: 14)
|
||||||
|
: const EdgeInsetsDirectional.only(
|
||||||
|
top: 14,
|
||||||
|
bottom: 14,
|
||||||
|
start: 12,
|
||||||
|
end: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: _currentListEmailAddress.isNotEmpty
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 12)
|
||||||
|
: EdgeInsets.zero,
|
||||||
|
borderRadius: 10,
|
||||||
|
borderSize: 1,
|
||||||
|
focusedBorderColor: AppColor.primaryColor,
|
||||||
|
enableBorder: true,
|
||||||
|
enableBorderColor:
|
||||||
|
_isDragging ? AppColor.primaryColor : AppColor.m3Neutral90,
|
||||||
|
minTextFieldWidth: 40.0,
|
||||||
|
resetTextOnSubmitted: true,
|
||||||
|
autoScrollToInput: false,
|
||||||
|
suggestionsBoxElevation: 20,
|
||||||
|
suggestionsBoxBackgroundColor: Colors.white,
|
||||||
|
suggestionsBoxRadius: 20,
|
||||||
|
suggestionsBoxMaxHeight: 350,
|
||||||
|
suggestionItemHeight: 60,
|
||||||
|
suggestionBoxWidth: _getSuggestionBoxWidth(constraints.maxWidth),
|
||||||
|
textStyle: ThemeUtils.textStyleBodyBody3(
|
||||||
|
color: AppColor.m3SurfaceBackground,
|
||||||
|
),
|
||||||
|
onFocusTagAction: _handleFocusTagAction,
|
||||||
|
onDeleteTagAction: _handleDeleteLatestTagAction,
|
||||||
|
onSelectOptionAction: _handleSelectOptionAction,
|
||||||
|
onSubmitted: _handleSubmitTagAction,
|
||||||
|
tagBuilder: (context, index) {
|
||||||
|
final currentEmailAddress = _currentListEmailAddress.elementAt(index);
|
||||||
|
final isLatestEmail =
|
||||||
|
currentEmailAddress == _currentListEmailAddress.last;
|
||||||
|
return DefaultAutocompleteTagItemWidget(
|
||||||
|
field: widget.field,
|
||||||
|
currentEmailAddress: currentEmailAddress,
|
||||||
|
currentListEmailAddress: _currentListEmailAddress,
|
||||||
|
collapsedListEmailAddress: _collapsedListEmailAddress,
|
||||||
|
iconClose: _imagePaths.icClose,
|
||||||
|
isLatestEmail: isLatestEmail,
|
||||||
|
isCollapsed: _isCollapse,
|
||||||
|
isLatestTagFocused: _lastTagFocused,
|
||||||
|
onDeleteTagAction: _handleDeleteTagAction,
|
||||||
|
onShowFullAction: widget.onShowFullListEmailAddressAction,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTagChanged: _handleOnTagChangeAction,
|
||||||
|
findSuggestions: (queryString) => _findSuggestions(
|
||||||
|
queryString,
|
||||||
|
limit: AppConfig.defaultLimitAutocomplete,
|
||||||
|
),
|
||||||
|
isLoadMoreOnlyOnce: true,
|
||||||
|
isLoadMoreReplaceAllOld: false,
|
||||||
|
loadMoreSuggestions: _findSuggestions,
|
||||||
|
suggestionBuilder: (
|
||||||
|
context,
|
||||||
|
tagEditorState,
|
||||||
|
suggestionEmailAddress,
|
||||||
|
index,
|
||||||
|
length,
|
||||||
|
highlight,
|
||||||
|
suggestionValid,
|
||||||
|
) {
|
||||||
|
return RecipientSuggestionItemWidget(
|
||||||
|
imagePaths: _imagePaths,
|
||||||
|
suggestionState: suggestionEmailAddress.state,
|
||||||
|
emailAddress: suggestionEmailAddress.emailAddress,
|
||||||
|
suggestionValid: suggestionValid,
|
||||||
|
highlight: highlight,
|
||||||
|
onSelectedAction: (emailAddress) {
|
||||||
|
_setStateSafety(() => _currentListEmailAddress.add(emailAddress));
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
tagEditorState.resetTextField();
|
||||||
|
tagEditorState.closeSuggestionBox();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onHandleKeyEventAction:
|
||||||
|
widget.nextFocusNode == null ? null : _onKeyEvent,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
return DragTarget<DraggableEmailAddress>(
|
||||||
|
onAcceptWithDetails: (draggableEmailAddress) => _onAcceptWithDetails(
|
||||||
|
draggableEmailAddress.data,
|
||||||
|
),
|
||||||
|
onLeave: (_) {
|
||||||
|
if (_isDragging) {
|
||||||
|
_setStateSafety(() => _isDragging = false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMove: (_) {
|
||||||
|
if (!_isDragging) {
|
||||||
|
_setStateSafety(() => _isDragging = true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (_, __, ___) => tagEditor,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return tagEditor;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (widget.padding != null) {
|
||||||
|
return Padding(padding: widget.padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onKeyEvent(KeyEvent event) {
|
||||||
|
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.tab) {
|
||||||
|
widget.nextFocusNode?.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _isCollapse =>
|
||||||
|
_currentListEmailAddress.length > 1 &&
|
||||||
|
widget.expandMode == ExpandMode.COLLAPSE;
|
||||||
|
|
||||||
|
List<EmailAddress> get _collapsedListEmailAddress => _isCollapse
|
||||||
|
? _currentListEmailAddress.sublist(0, 1)
|
||||||
|
: _currentListEmailAddress;
|
||||||
|
|
||||||
|
FutureOr<List<SuggestionEmailAddress>> _findSuggestions(
|
||||||
|
String query, {
|
||||||
|
int? limit,
|
||||||
|
}) async {
|
||||||
|
if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final processedQuery = query.trim();
|
||||||
|
if (processedQuery.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final displayedSuggestion =
|
||||||
|
List<SuggestionEmailAddress>.empty(growable: true);
|
||||||
|
if (processedQuery.length >= widget.minInputLengthAutocomplete &&
|
||||||
|
widget.onSuggestionEmailAddress != null) {
|
||||||
|
final listEmailAddress = await widget.onSuggestionEmailAddress!(
|
||||||
|
processedQuery,
|
||||||
|
limit: limit,
|
||||||
|
);
|
||||||
|
final listSuggestionEmailAddress =
|
||||||
|
listEmailAddress.map((emailAddress) => _toSuggestionEmailAddress(
|
||||||
|
emailAddress,
|
||||||
|
_currentListEmailAddress,
|
||||||
|
));
|
||||||
|
displayedSuggestion.addAll(listSuggestionEmailAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
displayedSuggestion.addAll(_matchedSuggestionEmailAddress(
|
||||||
|
processedQuery,
|
||||||
|
_currentListEmailAddress,
|
||||||
|
));
|
||||||
|
|
||||||
|
final currentTextOnTextField = widget.controller?.text ?? '';
|
||||||
|
if (currentTextOnTextField.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayedSuggestion.toSet().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isDuplicated(String inputEmail) {
|
||||||
|
if (inputEmail.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _currentListEmailAddress
|
||||||
|
.map((emailAddress) => emailAddress.email)
|
||||||
|
.whereNotNull()
|
||||||
|
.contains(inputEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
SuggestionEmailAddress _toSuggestionEmailAddress(
|
||||||
|
EmailAddress item,
|
||||||
|
List<EmailAddress> addedEmailAddresses,
|
||||||
|
) {
|
||||||
|
if (addedEmailAddresses.contains(item)) {
|
||||||
|
return SuggestionEmailAddress(
|
||||||
|
item,
|
||||||
|
state: SuggestionEmailState.duplicated,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return SuggestionEmailAddress(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<SuggestionEmailAddress> _matchedSuggestionEmailAddress(
|
||||||
|
String query,
|
||||||
|
List<EmailAddress> addedEmailAddress,
|
||||||
|
) {
|
||||||
|
return addedEmailAddress
|
||||||
|
.where((addedMail) => addedMail.emailAddress.contains(query))
|
||||||
|
.map((emailAddress) => SuggestionEmailAddress(
|
||||||
|
emailAddress,
|
||||||
|
state: SuggestionEmailState.duplicated,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateListEmailAddressAction() {
|
||||||
|
widget.onUpdateListEmailAddressAction?.call(
|
||||||
|
widget.field,
|
||||||
|
_currentListEmailAddress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleFocusTagAction(bool focused) {
|
||||||
|
_setStateSafety(() => _lastTagFocused = focused);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDeleteLatestTagAction() {
|
||||||
|
if (_currentListEmailAddress.isNotEmpty) {
|
||||||
|
_setStateSafety(_currentListEmailAddress.removeLast);
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDeleteTagAction(EmailAddress emailAddress) {
|
||||||
|
if (_currentListEmailAddress.isNotEmpty) {
|
||||||
|
_setStateSafety(() => _currentListEmailAddress.remove(emailAddress));
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSelectOptionAction(
|
||||||
|
SuggestionEmailAddress suggestionEmailAddress,
|
||||||
|
) {
|
||||||
|
if (!_isDuplicated(suggestionEmailAddress.emailAddress.emailAddress)) {
|
||||||
|
_setStateSafety(
|
||||||
|
() => _currentListEmailAddress.add(suggestionEmailAddress.emailAddress),
|
||||||
|
);
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSubmitTagAction(String value) {
|
||||||
|
final textTrim = value.trim();
|
||||||
|
if (textTrim.isEmpty) {
|
||||||
|
widget.onSearchAction?.call();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isDuplicated(textTrim)) {
|
||||||
|
_setStateSafety(
|
||||||
|
() => _currentListEmailAddress.add(EmailAddress(null, textTrim)),
|
||||||
|
);
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleOnTagChangeAction(String value) {
|
||||||
|
final textTrim = value.trim();
|
||||||
|
if (!_isDuplicated(textTrim)) {
|
||||||
|
_setStateSafety(
|
||||||
|
() => _currentListEmailAddress.add(EmailAddress(null, textTrim)),
|
||||||
|
);
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
}
|
||||||
|
_gapBetweenTagChangedAndFindSuggestion = Timer(
|
||||||
|
const Duration(seconds: 1),
|
||||||
|
() {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double? _getSuggestionBoxWidth(double maxWidth) {
|
||||||
|
if (maxWidth < ResponsiveUtils.minTabletWidth) {
|
||||||
|
final newWidth = min(maxWidth, 300.0);
|
||||||
|
return newWidth;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAcceptWithDetails(DraggableEmailAddress draggableEmailAddress) {
|
||||||
|
if (draggableEmailAddress.filterField != widget.field) {
|
||||||
|
if (!_currentListEmailAddress
|
||||||
|
.contains(draggableEmailAddress.emailAddress)) {
|
||||||
|
_setStateSafety(() {
|
||||||
|
_currentListEmailAddress.add(draggableEmailAddress.emailAddress);
|
||||||
|
_isDragging = false;
|
||||||
|
});
|
||||||
|
_updateListEmailAddressAction();
|
||||||
|
} else if (_isDragging) {
|
||||||
|
_setStateSafety(() => _isDragging = false);
|
||||||
|
}
|
||||||
|
widget.onRemoveDraggableEmailAddressAction?.call(draggableEmailAddress);
|
||||||
|
} else if (_isDragging) {
|
||||||
|
_setStateSafety(() => _isDragging = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setStateSafety(VoidCallback onAction) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(onAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-12
@@ -1,38 +1,38 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/core.dart';
|
import 'package:core/presentation/utils/style_utils.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:core/utils/direction_utils.dart';
|
import 'package:core/utils/direction_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/extensions/email_address_extension.dart';
|
import 'package:model/extensions/email_address_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_input_field_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/draggable_recipient_tag_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/draggable_recipient_tag_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart';
|
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/autocomplete_tag_item_web_style.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/autocomplete_tag_item_web_style.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/avatar_tag_item_widget.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/avatar_tag_item_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart';
|
|
||||||
|
|
||||||
class AutoCompleteTagItemWidgetWeb extends StatelessWidget {
|
class DefaultAutocompleteTagItemWidget extends StatelessWidget {
|
||||||
|
|
||||||
final bool isCollapsed;
|
final bool isCollapsed;
|
||||||
final bool isLatestTagFocused;
|
final bool isLatestTagFocused;
|
||||||
final bool isLatestEmail;
|
final bool isLatestEmail;
|
||||||
final AdvancedSearchFilterField field;
|
final FilterField field;
|
||||||
final EmailAddress currentEmailAddress;
|
final EmailAddress currentEmailAddress;
|
||||||
|
final String iconClose;
|
||||||
final List<EmailAddress> currentListEmailAddress;
|
final List<EmailAddress> currentListEmailAddress;
|
||||||
final List<EmailAddress> collapsedListEmailAddress;
|
final List<EmailAddress> collapsedListEmailAddress;
|
||||||
final OnDeleteTagAction? onDeleteTagAction;
|
final OnDeleteTagAction? onDeleteTagAction;
|
||||||
final OnShowFullListEmailAddressAction? onShowFullAction;
|
final OnShowFullListEmailAddressAction? onShowFullAction;
|
||||||
|
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
const DefaultAutocompleteTagItemWidget({
|
||||||
|
|
||||||
AutoCompleteTagItemWidgetWeb({
|
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.field,
|
required this.field,
|
||||||
required this.currentEmailAddress,
|
required this.currentEmailAddress,
|
||||||
required this.currentListEmailAddress,
|
required this.currentListEmailAddress,
|
||||||
required this.collapsedListEmailAddress,
|
required this.collapsedListEmailAddress,
|
||||||
|
required this.iconClose,
|
||||||
this.isCollapsed = false,
|
this.isCollapsed = false,
|
||||||
this.isLatestTagFocused = false,
|
this.isLatestTagFocused = false,
|
||||||
this.isLatestEmail = false,
|
this.isLatestEmail = false,
|
||||||
@@ -53,7 +53,7 @@ class AutoCompleteTagItemWidgetWeb extends StatelessWidget {
|
|||||||
child: Draggable<DraggableEmailAddress>(
|
child: Draggable<DraggableEmailAddress>(
|
||||||
data: DraggableEmailAddress(
|
data: DraggableEmailAddress(
|
||||||
emailAddress: currentEmailAddress,
|
emailAddress: currentEmailAddress,
|
||||||
prefix: field.getPrefixEmailAddress()
|
filterField: field,
|
||||||
),
|
),
|
||||||
feedback: DraggableRecipientTagWidget(
|
feedback: DraggableRecipientTagWidget(
|
||||||
emailAddress: currentEmailAddress,
|
emailAddress: currentEmailAddress,
|
||||||
@@ -82,7 +82,7 @@ class AutoCompleteTagItemWidgetWeb extends StatelessWidget {
|
|||||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
softWrap: CommonTextStyle.defaultSoftWrap,
|
||||||
),
|
),
|
||||||
deleteIcon: SvgPicture.asset(
|
deleteIcon: SvgPicture.asset(
|
||||||
_imagePaths.icClose,
|
iconClose,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DefaultCloseButtonWidget extends StatelessWidget {
|
||||||
|
final String iconClose;
|
||||||
|
final VoidCallback onTapActionCallback;
|
||||||
|
|
||||||
|
const DefaultCloseButtonWidget({
|
||||||
|
super.key,
|
||||||
|
required this.iconClose,
|
||||||
|
required this.onTapActionCallback,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PositionedDirectional(
|
||||||
|
top: 4,
|
||||||
|
end: 4,
|
||||||
|
child: TMailButtonWidget.fromIcon(
|
||||||
|
icon: iconClose,
|
||||||
|
iconSize: 24,
|
||||||
|
iconColor: AppColor.m3Tertiary,
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
borderRadius: 24,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
onTapActionCallback: onTapActionCallback,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/date_drop_down_button.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
typedef OnOpenDatPicker = void Function();
|
||||||
|
|
||||||
|
class DefaultDateDropDownFieldWidget extends StatelessWidget {
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
final List<EmailReceiveTimeType> receiveTimeTypes;
|
||||||
|
final EmailReceiveTimeType receiveTimeTypeSelected;
|
||||||
|
final OnReceiveTimeSelected onReceiveTimeSelected;
|
||||||
|
final OnOpenDatPicker onOpenDatPicker;
|
||||||
|
final DateTime? startDate;
|
||||||
|
final DateTime? endDate;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
const DefaultDateDropDownFieldWidget({
|
||||||
|
super.key,
|
||||||
|
required this.imagePaths,
|
||||||
|
required this.receiveTimeTypes,
|
||||||
|
required this.receiveTimeTypeSelected,
|
||||||
|
required this.onReceiveTimeSelected,
|
||||||
|
required this.onOpenDatPicker,
|
||||||
|
this.startDate,
|
||||||
|
this.endDate,
|
||||||
|
this.padding,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bodyWidget = Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: DateDropDownButton(
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
receiveTimeTypes: receiveTimeTypes,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
receiveTimeTypeSelected: receiveTimeTypeSelected,
|
||||||
|
onReceiveTimeSelected: onReceiveTimeSelected,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColor.m3Neutral90,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
child: TMailButtonWidget.fromIcon(
|
||||||
|
icon: imagePaths.icCalendarSB,
|
||||||
|
iconSize: 22,
|
||||||
|
iconColor: AppColor.steelGray400,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
tooltipMessage: AppLocalizations.of(context).selectDate,
|
||||||
|
onTapActionCallback: onOpenDatPicker,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (padding != null) {
|
||||||
|
return Padding(padding: padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
typedef OnTextSubmitted = void Function(String text);
|
||||||
|
typedef OnTextChange = void Function(String text);
|
||||||
|
|
||||||
|
class DefaultInputFieldWidget extends StatelessWidget {
|
||||||
|
final TextEditingController textEditingController;
|
||||||
|
final String? hintText;
|
||||||
|
final OnTextChange? onTextChange;
|
||||||
|
final OnTextSubmitted? onTextSubmitted;
|
||||||
|
|
||||||
|
const DefaultInputFieldWidget({
|
||||||
|
super.key,
|
||||||
|
required this.textEditingController,
|
||||||
|
this.hintText,
|
||||||
|
this.onTextChange,
|
||||||
|
this.onTextSubmitted,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFieldBuilder(
|
||||||
|
controller: textEditingController,
|
||||||
|
maxLines: 1,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
textStyle: ThemeUtils.textStyleBodyBody3(
|
||||||
|
color: AppColor.m3SurfaceBackground,
|
||||||
|
),
|
||||||
|
onTextSubmitted: onTextSubmitted,
|
||||||
|
onTextChange: onTextChange,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
constraints: const BoxConstraints(maxHeight: 40),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
contentPadding: const EdgeInsetsDirectional.only(start: 12, end: 8),
|
||||||
|
enabledBorder: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
borderSide: BorderSide(width: 1, color: AppColor.m3Neutral90),
|
||||||
|
),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
borderSide: BorderSide(width: 1, color: AppColor.m3Neutral90),
|
||||||
|
),
|
||||||
|
focusedBorder: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
borderSide: BorderSide(width: 1, color: AppColor.primaryColor),
|
||||||
|
),
|
||||||
|
hintText: hintText,
|
||||||
|
hintStyle: ThemeUtils.textStyleBodyBody3(color: AppColor.m3Tertiary),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
|
||||||
|
|
||||||
|
typedef OnTextSubmitted = void Function(String text);
|
||||||
|
typedef OnTextChange = void Function(String text);
|
||||||
|
|
||||||
|
class DefaultInputFieldWithTabKeyWidget extends StatelessWidget {
|
||||||
|
final TextEditingController textEditingController;
|
||||||
|
final FocusNode currentFocusNode;
|
||||||
|
final FocusNode? nextFocusNode;
|
||||||
|
final String? hintText;
|
||||||
|
final OnTextChange? onTextChange;
|
||||||
|
final OnTextSubmitted? onTextSubmitted;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
const DefaultInputFieldWithTabKeyWidget({
|
||||||
|
super.key,
|
||||||
|
required this.textEditingController,
|
||||||
|
required this.currentFocusNode,
|
||||||
|
this.nextFocusNode,
|
||||||
|
this.hintText,
|
||||||
|
this.onTextChange,
|
||||||
|
this.onTextSubmitted,
|
||||||
|
this.padding,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bodyWidget = KeyboardListener(
|
||||||
|
focusNode: currentFocusNode,
|
||||||
|
onKeyEvent: nextFocusNode == null ? null : _onKeyEvent,
|
||||||
|
child: DefaultInputFieldWidget(
|
||||||
|
textEditingController: textEditingController,
|
||||||
|
hintText: hintText,
|
||||||
|
onTextChange: onTextChange,
|
||||||
|
onTextSubmitted: onTextSubmitted,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (padding != null) {
|
||||||
|
return Padding(padding: padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onKeyEvent(KeyEvent event) {
|
||||||
|
if (event is KeyDownEvent &&
|
||||||
|
event.logicalKey == LogicalKeyboardKey.tab) {
|
||||||
|
nextFocusNode?.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DefaultLabelFieldWidget extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
final double? width;
|
||||||
|
|
||||||
|
const DefaultLabelFieldWidget({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
this.padding,
|
||||||
|
this.width,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bodyWidget = Container(
|
||||||
|
height: 38,
|
||||||
|
width: width,
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (padding != null) {
|
||||||
|
return Padding(padding: padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import 'package:permission_handler/permission_handler.dart';
|
|||||||
import 'package:super_tag_editor/tag_editor.dart';
|
import 'package:super_tag_editor/tag_editor.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/date_range_picker_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/date_range_picker_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
|
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart';
|
import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/state/get_device_contact_suggestions_state.dart';
|
import 'package:tmail_ui_user/features/composer/domain/state/get_device_contact_suggestions_state.dart';
|
||||||
@@ -21,9 +22,8 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete
|
|||||||
import 'package:tmail_ui_user/features/composer/domain/usecases/get_device_contact_suggestions_interactor.dart';
|
import 'package:tmail_ui_user/features/composer/domain/usecases/get_device_contact_suggestions_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/controller/input_field_focus_manager.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/controller/input_field_focus_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_arguments.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_time_type.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/session_extension.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/model/session_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||||
@@ -33,8 +33,8 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
||||||
GetDeviceContactSuggestionsInteractor? _getDeviceContactSuggestionsInteractor;
|
GetDeviceContactSuggestionsInteractor? _getDeviceContactSuggestionsInteractor;
|
||||||
|
|
||||||
final deletionDateFieldSelected = EmailRecoveryTimeType.last7Days.obs;
|
final deletionDateFieldSelected = EmailReceiveTimeType.last7Days.obs;
|
||||||
final receptionDateFieldSelected = EmailRecoveryTimeType.allTime.obs;
|
final receptionDateFieldSelected = EmailReceiveTimeType.allTime.obs;
|
||||||
final startDeletionDate = Rxn<DateTime>();
|
final startDeletionDate = Rxn<DateTime>();
|
||||||
final endDeletionDate = Rxn<DateTime>();
|
final endDeletionDate = Rxn<DateTime>();
|
||||||
final startReceptionDate = Rxn<DateTime>();
|
final startReceptionDate = Rxn<DateTime>();
|
||||||
@@ -47,6 +47,8 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
|
|
||||||
List<EmailAddress> listRecipients = <EmailAddress>[];
|
List<EmailAddress> listRecipients = <EmailAddress>[];
|
||||||
List<EmailAddress> listSenders = <EmailAddress>[];
|
List<EmailAddress> listSenders = <EmailAddress>[];
|
||||||
|
List<EmailReceiveTimeType> deletionDateTimeTypes = <EmailReceiveTimeType>[];
|
||||||
|
List<EmailReceiveTimeType> receptionDateTimeTypes = <EmailReceiveTimeType>[];
|
||||||
|
|
||||||
TextEditingController subjectFieldInputController = TextEditingController();
|
TextEditingController subjectFieldInputController = TextEditingController();
|
||||||
TextEditingController recipientsFieldInputController = TextEditingController();
|
TextEditingController recipientsFieldInputController = TextEditingController();
|
||||||
@@ -59,8 +61,7 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
EmailRecoveryArguments? arguments;
|
EmailRecoveryArguments? arguments;
|
||||||
AccountId? _accountId;
|
AccountId? _accountId;
|
||||||
Session? _session;
|
Session? _session;
|
||||||
|
int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete;
|
||||||
EmailRecoveryController();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@@ -76,6 +77,8 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
if (arguments != null) {
|
if (arguments != null) {
|
||||||
_accountId = arguments!.accountId;
|
_accountId = arguments!.accountId;
|
||||||
_session = arguments!.session;
|
_session = arguments!.session;
|
||||||
|
_setUpTimeTypesForFields(arguments!.session);
|
||||||
|
_setUpMinInputLengthAutocomplete();
|
||||||
}
|
}
|
||||||
if (PlatformInfo.isMobile) {
|
if (PlatformInfo.isMobile) {
|
||||||
Future.delayed(
|
Future.delayed(
|
||||||
@@ -87,13 +90,29 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
super.onReady();
|
super.onReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
int get minInputLengthAutocomplete {
|
void _setUpTimeTypesForFields(Session session) {
|
||||||
|
deletionDateTimeTypes = FilterField
|
||||||
|
.deletionDate
|
||||||
|
.getSupportedTimeTypes(session.getRestorationHorizonAsDateTime());
|
||||||
|
|
||||||
|
receptionDateTimeTypes = FilterField
|
||||||
|
.receptionDate
|
||||||
|
.getSupportedTimeTypes(session.getRestorationHorizonAsDateTime());
|
||||||
|
|
||||||
|
deletionDateFieldSelected.value = deletionDateTimeTypes.firstOrNull
|
||||||
|
?? EmailReceiveTimeType.last7Days;
|
||||||
|
deletionDateFieldSelected.value = deletionDateTimeTypes.firstOrNull
|
||||||
|
?? EmailReceiveTimeType.allTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setUpMinInputLengthAutocomplete() {
|
||||||
if (_session == null || _accountId == null) {
|
if (_session == null || _accountId == null) {
|
||||||
return AppConfig.defaultMinInputLengthAutocomplete;
|
minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete;
|
||||||
}
|
}
|
||||||
return getMinInputLengthAutocomplete(
|
minInputLengthAutocomplete = getMinInputLengthAutocomplete(
|
||||||
session: _session!,
|
session: _session!,
|
||||||
accountId: _accountId!);
|
accountId: _accountId!,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _checkContactPermission() async {
|
void _checkContactPermission() async {
|
||||||
@@ -156,8 +175,8 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
endDeletionDate.value,
|
endDeletionDate.value,
|
||||||
onCallbackAction: (startDate, endDate) {
|
onCallbackAction: (startDate, endDate) {
|
||||||
_updateDateRangeTime(
|
_updateDateRangeTime(
|
||||||
EmailRecoveryField.deletionDate,
|
FilterField.deletionDate,
|
||||||
EmailRecoveryTimeType.customRange,
|
EmailReceiveTimeType.customRange,
|
||||||
startDate: startDate,
|
startDate: startDate,
|
||||||
endDate: endDate,
|
endDate: endDate,
|
||||||
);
|
);
|
||||||
@@ -172,8 +191,8 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
endReceptionDate.value,
|
endReceptionDate.value,
|
||||||
onCallbackAction: (startDate, endDate) {
|
onCallbackAction: (startDate, endDate) {
|
||||||
_updateDateRangeTime(
|
_updateDateRangeTime(
|
||||||
EmailRecoveryField.receptionDate,
|
FilterField.receptionDate,
|
||||||
EmailRecoveryTimeType.customRange,
|
EmailReceiveTimeType.customRange,
|
||||||
startDate: startDate,
|
startDate: startDate,
|
||||||
endDate: endDate,
|
endDate: endDate,
|
||||||
);
|
);
|
||||||
@@ -182,14 +201,14 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateDateRangeTime(
|
void _updateDateRangeTime(
|
||||||
EmailRecoveryField field,
|
FilterField field,
|
||||||
EmailRecoveryTimeType recoveryTimeType,
|
EmailReceiveTimeType recoveryTimeType,
|
||||||
{
|
{
|
||||||
DateTime? startDate,
|
DateTime? startDate,
|
||||||
DateTime? endDate,
|
DateTime? endDate,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (field == EmailRecoveryField.deletionDate) {
|
if (field == FilterField.deletionDate) {
|
||||||
deletionDateFieldSelected.value = recoveryTimeType;
|
deletionDateFieldSelected.value = recoveryTimeType;
|
||||||
startDeletionDate.value = startDate;
|
startDeletionDate.value = startDate;
|
||||||
endDeletionDate.value = endDate;
|
endDeletionDate.value = endDate;
|
||||||
@@ -200,12 +219,12 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDeletionDateTypeSelected(BuildContext context, EmailRecoveryTimeType type) {
|
void onDeletionDateTypeSelected(BuildContext context, EmailReceiveTimeType type) {
|
||||||
if (type == EmailRecoveryTimeType.customRange) {
|
if (type == EmailReceiveTimeType.customRange) {
|
||||||
onSelectDeletionDateRange(context);
|
onSelectDeletionDateRange(context);
|
||||||
} else {
|
} else {
|
||||||
_updateDateRangeTime(
|
_updateDateRangeTime(
|
||||||
EmailRecoveryField.deletionDate,
|
FilterField.deletionDate,
|
||||||
type,
|
type,
|
||||||
startDate: type.toLatestUTCDate()?.value,
|
startDate: type.toLatestUTCDate()?.value,
|
||||||
endDate: DateTime.now(),
|
endDate: DateTime.now(),
|
||||||
@@ -213,12 +232,12 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onReceptionDateTypeSelected(BuildContext context, EmailRecoveryTimeType type) {
|
void onReceptionDateTypeSelected(BuildContext context, EmailReceiveTimeType type) {
|
||||||
if (type == EmailRecoveryTimeType.customRange) {
|
if (type == EmailReceiveTimeType.customRange) {
|
||||||
onSelectReceptionDateRange(context);
|
onSelectReceptionDateRange(context);
|
||||||
} else {
|
} else {
|
||||||
_updateDateRangeTime(
|
_updateDateRangeTime(
|
||||||
EmailRecoveryField.receptionDate,
|
FilterField.receptionDate,
|
||||||
type,
|
type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -278,12 +297,12 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showFullEmailAddress(EmailRecoveryField field) {
|
void showFullEmailAddress(FilterField field) {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case EmailRecoveryField.recipients:
|
case FilterField.recipients:
|
||||||
recipientsExpandMode.value = ExpandMode.EXPAND;
|
recipientsExpandMode.value = ExpandMode.EXPAND;
|
||||||
break;
|
break;
|
||||||
case EmailRecoveryField.sender:
|
case FilterField.sender:
|
||||||
senderExpandMode.value = ExpandMode.EXPAND;
|
senderExpandMode.value = ExpandMode.EXPAND;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -292,15 +311,15 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateListEmailAddress(
|
void updateListEmailAddress(
|
||||||
EmailRecoveryField field,
|
FilterField field,
|
||||||
List<EmailAddress> listEmailAddress,
|
List<EmailAddress> listEmailAddress,
|
||||||
) {
|
) {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case EmailRecoveryField.recipients:
|
case FilterField.recipients:
|
||||||
listRecipients = List.from(listEmailAddress);
|
listRecipients = List.from(listEmailAddress);
|
||||||
log('EmailRecoveryController::updateListEmailAddress(): listRecipients: ${listRecipients.first}');
|
log('EmailRecoveryController::updateListEmailAddress(): listRecipients: ${listRecipients.first}');
|
||||||
break;
|
break;
|
||||||
case EmailRecoveryField.sender:
|
case FilterField.sender:
|
||||||
listSenders = List.from(listEmailAddress);
|
listSenders = List.from(listEmailAddress);
|
||||||
log('EmailRecoveryController::updateListEmailAddress(): listSenders: ${listSenders.first}');
|
log('EmailRecoveryController::updateListEmailAddress(): listSenders: ${listSenders.first}');
|
||||||
break;
|
break;
|
||||||
@@ -316,7 +335,7 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
UTCDate? deletedAfter;
|
UTCDate? deletedAfter;
|
||||||
UTCDate? receivedBefore;
|
UTCDate? receivedBefore;
|
||||||
UTCDate? receivedAfter;
|
UTCDate? receivedAfter;
|
||||||
if (deletionDateFieldSelected.value == EmailRecoveryTimeType.customRange) {
|
if (deletionDateFieldSelected.value == EmailReceiveTimeType.customRange) {
|
||||||
deletedBefore = endDeletionDate.value?.toUTCDate();
|
deletedBefore = endDeletionDate.value?.toUTCDate();
|
||||||
deletedAfter = startDeletionDate.value?.toUTCDate();
|
deletedAfter = startDeletionDate.value?.toUTCDate();
|
||||||
receivedBefore = endReceptionDate.value?.toUTCDate();
|
receivedBefore = endReceptionDate.value?.toUTCDate();
|
||||||
@@ -358,7 +377,7 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
DateTime getRestorationHorizonAsDateTime() => arguments!.session.getRestorationHorizonAsDateTime();
|
DateTime getRestorationHorizonAsDateTime() => arguments!.session.getRestorationHorizonAsDateTime();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void onClose() {
|
||||||
focusManager.subjectFieldFocusNode.removeListener(_onSubjectFieldFocusChanged);
|
focusManager.subjectFieldFocusNode.removeListener(_onSubjectFieldFocusChanged);
|
||||||
focusManager.recipientsFieldFocusNode.removeListener(_onRecipientsFieldFocusChanged);
|
focusManager.recipientsFieldFocusNode.removeListener(_onRecipientsFieldFocusChanged);
|
||||||
focusManager.senderFieldFocusNode.removeListener(_onSenderFieldFocusChanged);
|
focusManager.senderFieldFocusNode.removeListener(_onSenderFieldFocusChanged);
|
||||||
@@ -366,6 +385,10 @@ class EmailRecoveryController extends BaseController with DateRangePickerMixin {
|
|||||||
recipientsFieldInputController.dispose();
|
recipientsFieldInputController.dispose();
|
||||||
senderFieldInputController.dispose();
|
senderFieldInputController.dispose();
|
||||||
focusManager.dispose();
|
focusManager.dispose();
|
||||||
super.dispose();
|
listSenders.clear();
|
||||||
|
listRecipients.clear();
|
||||||
|
deletionDateTimeTypes.clear();
|
||||||
|
receptionDateTimeTypes.clear();
|
||||||
|
super.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
import 'package:core/presentation/views/responsive/responsive_widget.dart';
|
import 'package:core/presentation/views/responsive/responsive_widget.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/email_recovery_view_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/email_recovery_form/email_recovery_form_desktop_builder.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/email_recovery_form/email_recovery_form_desktop_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/email_recovery_form/email_recovery_form_mobile_builder.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/email_recovery_form/email_recovery_form_mobile_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/email_recovery_form/email_recovery_form_tablet_builder.dart';
|
|
||||||
|
|
||||||
class EmailRecoveryView extends GetWidget<EmailRecoveryController> {
|
class EmailRecoveryView extends GetWidget<EmailRecoveryController> {
|
||||||
@override
|
@override
|
||||||
@@ -20,66 +17,8 @@ class EmailRecoveryView extends GetWidget<EmailRecoveryController> {
|
|||||||
return PointerInterceptor(
|
return PointerInterceptor(
|
||||||
child: ResponsiveWidget(
|
child: ResponsiveWidget(
|
||||||
responsiveUtils: controller.responsiveUtils,
|
responsiveUtils: controller.responsiveUtils,
|
||||||
mobile: Scaffold(
|
mobile: EmailRecoveryFormMobileBuilder(controller),
|
||||||
backgroundColor: PlatformInfo.isWeb ? Colors.black.withAlpha(24) : Colors.black38,
|
tablet: EmailRecoveryFormDesktopBuilder(controller),
|
||||||
body: GestureDetector(
|
|
||||||
onTap: () => FocusScope.of(context).unfocus(),
|
|
||||||
child: SafeArea(
|
|
||||||
bottom: false,
|
|
||||||
left: false,
|
|
||||||
right: false,
|
|
||||||
child: Container(
|
|
||||||
decoration: EmailRecoveryViewStyles.backgroundDecorationMobile,
|
|
||||||
margin: EmailRecoveryViewStyles.backgroundMarginMobile,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: EmailRecoveryViewStyles.clipRRectBorderRadiusMobile,
|
|
||||||
child: SafeArea(
|
|
||||||
child: EmailRecoveryFormMobileBuilder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
tablet: Scaffold(
|
|
||||||
backgroundColor: Colors.black.withAlpha(24),
|
|
||||||
body: GestureDetector(
|
|
||||||
onTap: () => FocusScope.of(context).unfocus(),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Container(
|
|
||||||
decoration: EmailRecoveryViewStyles.backgroundDecorationMobile,
|
|
||||||
width: double.infinity,
|
|
||||||
height: controller.responsiveUtils.getSizeScreenHeight(context) * 0.7,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: EmailRecoveryViewStyles.clipRRectBorderRadiusMobile,
|
|
||||||
child: EmailRecoveryFormTabletBuilder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
desktop: Scaffold(
|
|
||||||
backgroundColor: Colors.black.withAlpha(24),
|
|
||||||
body: GestureDetector(
|
|
||||||
onTap:() => FocusScope.of(context).unfocus(),
|
|
||||||
child: Center(
|
|
||||||
child: Card(
|
|
||||||
color: Colors.transparent,
|
|
||||||
shape: EmailRecoveryViewStyles.shapeBorderDesktop,
|
|
||||||
child: Container(
|
|
||||||
decoration: EmailRecoveryViewStyles.backgroundDecorationDesktop,
|
|
||||||
width: controller.responsiveUtils.getSizeScreenWidth(context) * 0.38,
|
|
||||||
height: controller.responsiveUtils.getSizeScreenHeight(context) * 0.6,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: EmailRecoveryViewStyles.clipRRectBorderRadiusDesktop,
|
|
||||||
child: EmailRecoveryFormDesktopBuilder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
import 'package:model/mailbox/expand_mode.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
||||||
|
|
||||||
|
extension HandleDragEmailTagBetweenFieldExtension on EmailRecoveryController {
|
||||||
|
void removeDraggableEmailAddress(DraggableEmailAddress draggableEmailAddress) {
|
||||||
|
switch(draggableEmailAddress.filterField) {
|
||||||
|
case FilterField.recipients:
|
||||||
|
listRecipients.remove(draggableEmailAddress.emailAddress);
|
||||||
|
recipientsExpandMode.value = ExpandMode.EXPAND;
|
||||||
|
recipientsExpandMode.refresh();
|
||||||
|
break;
|
||||||
|
case FilterField.sender:
|
||||||
|
listSenders.remove(draggableEmailAddress.emailAddress);
|
||||||
|
senderExpandMode.value = ExpandMode.EXPAND;
|
||||||
|
senderExpandMode.refresh();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import 'package:core/utils/platform_info.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class EmailRecoveryViewStyles {
|
|
||||||
static const BoxDecoration backgroundDecorationMobile = BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(16),
|
|
||||||
topRight: Radius.circular(16),
|
|
||||||
),
|
|
||||||
color: Colors.white,
|
|
||||||
);
|
|
||||||
static const BoxDecoration backgroundDecorationDesktop = BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
color: Colors.white,
|
|
||||||
);
|
|
||||||
|
|
||||||
static EdgeInsetsGeometry backgroundMarginMobile = EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0);
|
|
||||||
|
|
||||||
static const BorderRadiusGeometry clipRRectBorderRadiusMobile = BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(16),
|
|
||||||
topRight: Radius.circular(16),
|
|
||||||
);
|
|
||||||
static const BorderRadiusGeometry clipRRectBorderRadiusDesktop = BorderRadius.all(Radius.circular(16));
|
|
||||||
|
|
||||||
static const ShapeBorder shapeBorderDesktop = RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:core/presentation/views/checkbox/default_labeled_checkbox.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger_mobile.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|
||||||
|
|
||||||
typedef OnChangedHasAttachment = void Function(bool? value);
|
|
||||||
|
|
||||||
class CheckBoxHasAttachmentWidget extends StatelessWidget {
|
|
||||||
final bool hasAttachmentValue;
|
|
||||||
final FocusNode currentFocusNode;
|
|
||||||
final FocusNode? nextFocusNode;
|
|
||||||
final OnChangedHasAttachment onChanged;
|
|
||||||
|
|
||||||
const CheckBoxHasAttachmentWidget({
|
|
||||||
super.key,
|
|
||||||
required this.hasAttachmentValue,
|
|
||||||
required this.currentFocusNode,
|
|
||||||
required this.onChanged,
|
|
||||||
this.nextFocusNode,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return KeyboardListener(
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
onKeyEvent: (event) {
|
|
||||||
if (event.logicalKey == LogicalKeyboardKey.tab) {
|
|
||||||
nextFocusNode?.requestFocus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: DefaultLabeledCheckbox(
|
|
||||||
label: AppLocalizations.of(context).hasAttachment,
|
|
||||||
focusNode: currentFocusNode,
|
|
||||||
value: hasAttachmentValue,
|
|
||||||
activeColor: AppColor.primaryColor,
|
|
||||||
onChanged: onChanged,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-139
@@ -1,139 +0,0 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:core/presentation/utils/style_utils.dart';
|
|
||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_time_type.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/date_selection_field/date_selection_dropdown_button_styles.dart';
|
|
||||||
|
|
||||||
typedef OnRecoveryTimeSelected = void Function(EmailRecoveryTimeType recoveryTime);
|
|
||||||
|
|
||||||
class DateSelectionDropDownButton extends StatelessWidget {
|
|
||||||
|
|
||||||
final ImagePaths imagePaths;
|
|
||||||
final List<EmailRecoveryTimeType> items;
|
|
||||||
final DateTime? startDate;
|
|
||||||
final DateTime? endDate;
|
|
||||||
final EmailRecoveryTimeType? recoveryTimeSelected;
|
|
||||||
final OnRecoveryTimeSelected? onRecoveryTimeSelected;
|
|
||||||
|
|
||||||
const DateSelectionDropDownButton({
|
|
||||||
super.key,
|
|
||||||
required this.imagePaths,
|
|
||||||
required this.items,
|
|
||||||
this.startDate,
|
|
||||||
this.endDate,
|
|
||||||
this.recoveryTimeSelected,
|
|
||||||
this.onRecoveryTimeSelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DropdownButtonHideUnderline(
|
|
||||||
child: PointerInterceptor(
|
|
||||||
child: DropdownButton2<EmailRecoveryTimeType>(
|
|
||||||
isExpanded: true,
|
|
||||||
items: items
|
|
||||||
.map((item) => _buildItemMenu(context, item))
|
|
||||||
.toList(),
|
|
||||||
value: recoveryTimeSelected,
|
|
||||||
customButton: Container(
|
|
||||||
height: DateSelectionDropdownButtonStyles.height,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: DateSelectionDropdownButtonStyles.borderRadius,
|
|
||||||
border: Border.all(
|
|
||||||
color: AppColor.colorInputBorderCreateMailbox,
|
|
||||||
width: DateSelectionDropdownButtonStyles.borderWidth,
|
|
||||||
),
|
|
||||||
color: AppColor.colorInputBackgroundCreateMailbox
|
|
||||||
),
|
|
||||||
padding: DateSelectionDropdownButtonStyles.padding,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
recoveryTimeSelected?.getTitle(context, startDate: startDate, endDate: endDate) ?? '',
|
|
||||||
style: DateSelectionDropdownButtonStyles.selectedValueTexStyle,
|
|
||||||
maxLines: 1,
|
|
||||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
|
||||||
overflow: CommonTextStyle.defaultTextOverFlow,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
SvgPicture.asset(imagePaths.icDropDown)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
onRecoveryTimeSelected?.call(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buttonStyleData: ButtonStyleData(
|
|
||||||
height: DateSelectionDropdownButtonStyles.height,
|
|
||||||
padding: DateSelectionDropdownButtonStyles.padding,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: DateSelectionDropdownButtonStyles.borderRadius,
|
|
||||||
border: Border.all(
|
|
||||||
color: AppColor.colorInputBorderCreateMailbox,
|
|
||||||
width: DateSelectionDropdownButtonStyles.borderWidth,
|
|
||||||
),
|
|
||||||
color: AppColor.colorInputBackgroundCreateMailbox
|
|
||||||
)
|
|
||||||
),
|
|
||||||
dropdownStyleData: DropdownStyleData(
|
|
||||||
maxHeight: DateSelectionDropdownButtonStyles.dropdownMaxHeight,
|
|
||||||
decoration: DateSelectionDropdownButtonStyles.dropdownDecoration,
|
|
||||||
elevation: DateSelectionDropdownButtonStyles.dropdownElevation,
|
|
||||||
offset: DateSelectionDropdownButtonStyles.dropdownOffset,
|
|
||||||
scrollbarTheme: ScrollbarThemeData(
|
|
||||||
radius: DateSelectionDropdownButtonStyles.scrollbarRadius,
|
|
||||||
thickness: WidgetStateProperty.all<double>(DateSelectionDropdownButtonStyles.scrollbarThickness),
|
|
||||||
thumbVisibility: WidgetStateProperty.all<bool>(true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
iconStyleData: IconStyleData(
|
|
||||||
icon: SvgPicture.asset(imagePaths.icDropDown),
|
|
||||||
),
|
|
||||||
menuItemStyleData: DateSelectionDropdownButtonStyles.menuItemStyleData
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenuItem<EmailRecoveryTimeType> _buildItemMenu(
|
|
||||||
BuildContext context,
|
|
||||||
EmailRecoveryTimeType recoveryTime
|
|
||||||
) {
|
|
||||||
return DropdownMenuItem<EmailRecoveryTimeType>(
|
|
||||||
value: recoveryTime,
|
|
||||||
child: PointerInterceptor(
|
|
||||||
child: Container(
|
|
||||||
color: Colors.transparent,
|
|
||||||
height: DateSelectionDropdownButtonStyles.height,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
recoveryTime.getTitle(context),
|
|
||||||
style: DateSelectionDropdownButtonStyles.selectedValueTexStyle,
|
|
||||||
maxLines: 1,
|
|
||||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
|
||||||
overflow: CommonTextStyle.defaultTextOverFlow,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
if (recoveryTime == recoveryTimeSelected)
|
|
||||||
SvgPicture.asset(
|
|
||||||
imagePaths.icChecked,
|
|
||||||
width: DateSelectionDropdownButtonStyles.checkedIconSize,
|
|
||||||
height: DateSelectionDropdownButtonStyles.checkedIconSize,
|
|
||||||
fit: BoxFit.fill
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_time_type.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/date_selection_field/date_selection_field_styles.dart';
|
|
||||||
|
|
||||||
class DateSelectionFieldMobileWidget extends StatelessWidget {
|
|
||||||
final EmailRecoveryField field;
|
|
||||||
final EmailRecoveryTimeType recoveryTimeSelected;
|
|
||||||
final ImagePaths imagePaths;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final DateTime? startDate;
|
|
||||||
final DateTime? endDate;
|
|
||||||
|
|
||||||
const DateSelectionFieldMobileWidget({
|
|
||||||
super.key,
|
|
||||||
required this.field,
|
|
||||||
required this.recoveryTimeSelected,
|
|
||||||
required this.imagePaths,
|
|
||||||
this.onTap,
|
|
||||||
this.startDate,
|
|
||||||
this.endDate,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: DateSelectionFieldStyles.paddingMobile,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
field.getTitle(context),
|
|
||||||
style: field.getTitleTextStyle(),
|
|
||||||
),
|
|
||||||
const SizedBox(height: DateSelectionFieldStyles.titleSpaceMobile),
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: DateSelectionFieldStyles.height,
|
|
||||||
decoration: const ShapeDecoration(
|
|
||||||
color: AppColor.loginTextFieldBackgroundColor,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
side: BorderSide(
|
|
||||||
width: DateSelectionFieldStyles.borderWidthMobile,
|
|
||||||
color: AppColor.colorInputBorderCreateMailbox
|
|
||||||
),
|
|
||||||
borderRadius: DateSelectionFieldStyles.borderRadius
|
|
||||||
)
|
|
||||||
),
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
radius: DateSelectionFieldStyles.radius,
|
|
||||||
onTap: onTap,
|
|
||||||
child: Padding(
|
|
||||||
padding: DateSelectionFieldStyles.inputFieldMobilePadding,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
recoveryTimeSelected.getTitle(
|
|
||||||
context,
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: endDate
|
|
||||||
),
|
|
||||||
style: field.getHintTextStyle(),
|
|
||||||
),
|
|
||||||
SvgPicture.asset(
|
|
||||||
imagePaths.icCalendar,
|
|
||||||
width: DateSelectionFieldStyles.icCalendarSize,
|
|
||||||
height: DateSelectionFieldStyles.icCalendarSize,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
|
||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_time_type.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/date_selection_field/date_selection_field_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/date_selection_field/date_selection_dropdown_button.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|
||||||
|
|
||||||
class DateSelectionFieldWebWidget extends StatelessWidget {
|
|
||||||
final EmailRecoveryField field;
|
|
||||||
final ResponsiveUtils responsiveUtils;
|
|
||||||
final ImagePaths imagePaths;
|
|
||||||
final DateTime? startDate;
|
|
||||||
final DateTime? endDate;
|
|
||||||
final EmailRecoveryTimeType? recoveryTimeSelected;
|
|
||||||
final OnRecoveryTimeSelected? onRecoveryTimeSelected;
|
|
||||||
final VoidCallback? onTapCalendar;
|
|
||||||
final DateTime restorationHorizon;
|
|
||||||
|
|
||||||
const DateSelectionFieldWebWidget({
|
|
||||||
super.key,
|
|
||||||
required this.field,
|
|
||||||
required this.responsiveUtils,
|
|
||||||
required this.imagePaths,
|
|
||||||
this.startDate,
|
|
||||||
this.endDate,
|
|
||||||
this.recoveryTimeSelected,
|
|
||||||
this.onRecoveryTimeSelected,
|
|
||||||
this.onTapCalendar,
|
|
||||||
required this.restorationHorizon
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: DateSelectionFieldStyles.padding,
|
|
||||||
child: SizedBox(
|
|
||||||
height: DateSelectionFieldStyles.height,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: DateSelectionFieldStyles.titleWidth,
|
|
||||||
child: Text(
|
|
||||||
field.getTitle(context),
|
|
||||||
style: field.getTitleTextStyle(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: DateSelectionFieldStyles.titleSpace),
|
|
||||||
Expanded(
|
|
||||||
child: DateSelectionDropDownButton(
|
|
||||||
imagePaths: imagePaths,
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: endDate,
|
|
||||||
recoveryTimeSelected: recoveryTimeSelected,
|
|
||||||
onRecoveryTimeSelected: onRecoveryTimeSelected,
|
|
||||||
items: field.getSupportedTimeTypes(restorationHorizon),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: DateSelectionFieldStyles.icCalenderSpace),
|
|
||||||
buildIconWeb(
|
|
||||||
icon: SvgPicture.asset(
|
|
||||||
imagePaths.icCalendarSB,
|
|
||||||
width: DateSelectionFieldStyles.icCalendarSize,
|
|
||||||
height: DateSelectionFieldStyles.icCalendarSize,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
tooltip: AppLocalizations.of(context).selectDate,
|
|
||||||
iconPadding: EdgeInsets.zero,
|
|
||||||
onTap: onTapCalendar
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+247
-132
@@ -1,153 +1,268 @@
|
|||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:core/presentation/views/checkbox/custom_icon_labeled_checkbox.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_input_field_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_date_drop_down_field_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_label_field_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/extension/handle_drag_email_tag_between_field_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/email_recovery_form_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/check_box_has_attachment_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/date_selection_field/date_selection_field_web_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/limits_banner.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/limits_banner.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/list_button_widget.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/list_button_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_suggestion_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class EmailRecoveryFormDesktopBuilder extends GetWidget<EmailRecoveryController> {
|
class EmailRecoveryFormDesktopBuilder extends StatelessWidget {
|
||||||
@override
|
|
||||||
final controller = Get.find<EmailRecoveryController>();
|
|
||||||
|
|
||||||
EmailRecoveryFormDesktopBuilder({super.key});
|
final EmailRecoveryController controller;
|
||||||
|
|
||||||
|
const EmailRecoveryFormDesktopBuilder(this.controller, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
final appLocalizations = AppLocalizations.of(context);
|
||||||
children: [
|
|
||||||
Column(
|
return Scaffold(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
backgroundColor: Colors.black.withOpacity(0.20),
|
||||||
children: [
|
body: GestureDetector(
|
||||||
Container(
|
onTap: FocusScope.of(context).unfocus,
|
||||||
padding: EmailRecoveryFormStyles.padding,
|
child: Center(
|
||||||
alignment: Alignment.center,
|
child: Card(
|
||||||
child: Text(
|
shape: const RoundedRectangleBorder(
|
||||||
AppLocalizations.of(context).recoverDeletedMessages,
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
style: EmailRecoveryFormStyles.titleTextStyle,
|
),
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
width: min(
|
||||||
Expanded(
|
controller.responsiveUtils.getSizeScreenWidth(context),
|
||||||
child: SingleChildScrollView(
|
576,
|
||||||
physics: const ClampingScrollPhysics(),
|
),
|
||||||
child: Padding(
|
clipBehavior: Clip.antiAlias,
|
||||||
padding: EmailRecoveryFormStyles.inputAreaPadding,
|
child: Stack(
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
LimitsBanner(
|
|
||||||
bannerContent: AppLocalizations.of(context).recoverDeletedMessagesBannerContent(controller.getRestorationHorizonAsString()),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Obx(() => DateSelectionFieldWebWidget(
|
|
||||||
field: EmailRecoveryField.deletionDate,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
startDate: controller.startDeletionDate.value,
|
|
||||||
endDate: controller.endDeletionDate.value,
|
|
||||||
recoveryTimeSelected: controller.deletionDateFieldSelected.value,
|
|
||||||
onTapCalendar: () => controller.onSelectDeletionDateRange(context),
|
|
||||||
onRecoveryTimeSelected: (type) => controller.onDeletionDateTypeSelected(context, type),
|
|
||||||
restorationHorizon: controller.getRestorationHorizonAsDateTime(),
|
|
||||||
)),
|
|
||||||
Obx(() => DateSelectionFieldWebWidget(
|
|
||||||
field: EmailRecoveryField.receptionDate,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
startDate: controller.startReceptionDate.value,
|
|
||||||
endDate: controller.endReceptionDate.value,
|
|
||||||
recoveryTimeSelected: controller.receptionDateFieldSelected.value,
|
|
||||||
onTapCalendar: () => controller.onSelectReceptionDateRange(context),
|
|
||||||
onRecoveryTimeSelected: (type) => controller.onReceptionDateTypeSelected(context, type),
|
|
||||||
restorationHorizon: controller.getRestorationHorizonAsDateTime(),
|
|
||||||
)),
|
|
||||||
TextInputFieldWidget(
|
|
||||||
field: EmailRecoveryField.subject,
|
|
||||||
textEditingController: controller.subjectFieldInputController,
|
|
||||||
currentFocusNode: controller.focusManager.subjectFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.recipientsFieldFocusNode,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
),
|
|
||||||
Obx(() => TextInputFieldSuggestionWidget(
|
|
||||||
keyTagEditor: controller.recipientsFieldKey,
|
|
||||||
field: EmailRecoveryField.recipients,
|
|
||||||
listEmailAddress: controller.listRecipients,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
expandMode: controller.recipientsExpandMode.value,
|
|
||||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
|
||||||
textEditingController: controller.recipientsFieldInputController,
|
|
||||||
focusNode: controller.focusManager.recipientsFieldFocusNode,
|
|
||||||
nextFocusNode: controller.listSenders.length != 1
|
|
||||||
? controller.focusManager.senderFieldFocusNode
|
|
||||||
: controller.focusManager.attachmentCheckboxFocusNode,
|
|
||||||
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
|
||||||
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
|
||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
|
||||||
)),
|
|
||||||
Obx(() => TextInputFieldSuggestionWidget(
|
|
||||||
keyTagEditor: controller.senderFieldKey,
|
|
||||||
field: EmailRecoveryField.sender,
|
|
||||||
listEmailAddress: controller.listSenders,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
expandMode: controller.senderExpandMode.value,
|
|
||||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
|
||||||
textEditingController: controller.senderFieldInputController,
|
|
||||||
focusNode: controller.focusManager.senderFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
|
||||||
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
|
||||||
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
|
||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EmailRecoveryFormStyles.bottomAreaPadding,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Obx(() => CheckBoxHasAttachmentWidget(
|
FocusTraversalGroup(
|
||||||
hasAttachmentValue: controller.hasAttachment.value,
|
child: Column(
|
||||||
currentFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
onChanged: (value) => controller.onChangeHasAttachment(value),
|
mainAxisSize: MainAxisSize.min,
|
||||||
)),
|
children: [
|
||||||
SizedBox(width: controller.responsiveUtils.getDeviceWidth(context) * 0.04),
|
Center(
|
||||||
Expanded(
|
child: Padding(
|
||||||
child: ListButtonWidget(
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
onCancel: () => controller.closeView(context),
|
horizontal: 16,
|
||||||
onRestore: () => controller.onRestore(context),
|
vertical: 24
|
||||||
responsiveUtils: controller.responsiveUtils,
|
),
|
||||||
|
child: Text(
|
||||||
|
appLocalizations.recoverDeletedMessages,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: ThemeUtils.textStyleM3HeadlineSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
LimitsBanner(
|
||||||
|
bannerContent: appLocalizations
|
||||||
|
.dialogRecoverDeletedMessagesDescription(
|
||||||
|
controller.getRestorationHorizonAsString(),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 32,
|
||||||
|
),
|
||||||
|
isCenter: true,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.deletionDate.getTitle(appLocalizations),
|
||||||
|
width: 112,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => DefaultDateDropDownFieldWidget(
|
||||||
|
imagePaths: controller.imagePaths,
|
||||||
|
startDate: controller.startDeletionDate.value,
|
||||||
|
endDate: controller.endDeletionDate.value,
|
||||||
|
onReceiveTimeSelected: (receiveTimeType) {
|
||||||
|
controller.onDeletionDateTypeSelected(
|
||||||
|
context,
|
||||||
|
receiveTimeType,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onOpenDatPicker: () =>
|
||||||
|
controller.onSelectDeletionDateRange(context),
|
||||||
|
receiveTimeTypes: controller.deletionDateTimeTypes,
|
||||||
|
receiveTimeTypeSelected:
|
||||||
|
controller.deletionDateFieldSelected.value,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.receptionDate.getTitle(appLocalizations),
|
||||||
|
width: 112,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => DefaultDateDropDownFieldWidget(
|
||||||
|
imagePaths: controller.imagePaths,
|
||||||
|
startDate: controller.startReceptionDate.value,
|
||||||
|
endDate: controller.endReceptionDate.value,
|
||||||
|
onReceiveTimeSelected: (receiveTimeType) {
|
||||||
|
controller.onReceptionDateTypeSelected(
|
||||||
|
context,
|
||||||
|
receiveTimeType,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onOpenDatPicker: () =>
|
||||||
|
controller.onSelectReceptionDateRange(context),
|
||||||
|
receiveTimeTypes: controller.receptionDateTimeTypes,
|
||||||
|
receiveTimeTypeSelected:
|
||||||
|
controller.receptionDateFieldSelected.value,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.subject.getTitle(appLocalizations),
|
||||||
|
width: 112,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: DefaultInputFieldWithTabKeyWidget(
|
||||||
|
textEditingController: controller.subjectFieldInputController,
|
||||||
|
currentFocusNode: controller.focusManager.subjectFieldFocusNode,
|
||||||
|
nextFocusNode: controller.focusManager.recipientsFieldFocusNode,
|
||||||
|
hintText: FilterField.recoverSubject.getHintText(appLocalizations),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.recipients.getTitle(appLocalizations),
|
||||||
|
width: 112,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => DefaultAutocompleteInputFieldWidget(
|
||||||
|
field: FilterField.recipients,
|
||||||
|
listEmailAddress: controller.listRecipients,
|
||||||
|
expandMode: controller.recipientsExpandMode.value,
|
||||||
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
|
controller: controller.recipientsFieldInputController,
|
||||||
|
focusNode: controller.focusManager.recipientsFieldFocusNode,
|
||||||
|
nextFocusNode: controller.focusManager.senderFieldFocusNode,
|
||||||
|
keyTagEditor: controller.recipientsFieldKey,
|
||||||
|
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
||||||
|
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
||||||
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.sender.getTitle(appLocalizations),
|
||||||
|
width: 112,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => DefaultAutocompleteInputFieldWidget(
|
||||||
|
field: FilterField.sender,
|
||||||
|
listEmailAddress: controller.listSenders,
|
||||||
|
expandMode: controller.senderExpandMode.value,
|
||||||
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
|
controller: controller.senderFieldInputController,
|
||||||
|
focusNode: controller.focusManager.senderFieldFocusNode,
|
||||||
|
nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
||||||
|
keyTagEditor: controller.senderFieldKey,
|
||||||
|
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
||||||
|
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
||||||
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Obx(() => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: CustomIconLabeledCheckbox(
|
||||||
|
label: AppLocalizations.of(context).hasAttachment,
|
||||||
|
svgIconPath: controller.imagePaths.icCheckboxUnselected,
|
||||||
|
selectedSvgIconPath: controller.imagePaths.icCheckboxSelected,
|
||||||
|
focusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
||||||
|
value: controller.hasAttachment.value,
|
||||||
|
onChanged: controller.onChangeHasAttachment,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListButtonWidget(
|
||||||
|
onCancel: () => controller.closeView(context),
|
||||||
|
onRestore: () => controller.onRestore(context),
|
||||||
|
nextFocusNode: controller.focusManager.subjectFieldFocusNode,
|
||||||
|
isMobile: false,
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 37,
|
||||||
|
vertical: 24,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
DefaultCloseButtonWidget(
|
||||||
|
iconClose: controller.imagePaths.icCloseDialog,
|
||||||
|
onTapActionCallback: () => controller.closeView(context),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: EmailRecoveryFormStyles.iconClosePositionTop,
|
|
||||||
right: EmailRecoveryFormStyles.iconClosePositionRight,
|
|
||||||
child: buildIconWeb(
|
|
||||||
icon: SvgPicture.asset(
|
|
||||||
controller.imagePaths.icClose,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
),
|
),
|
||||||
tooltip: AppLocalizations.of(context).close,
|
|
||||||
onTap: () => controller.closeView(context),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+169
-121
@@ -1,138 +1,186 @@
|
|||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
import 'package:core/presentation/views/checkbox/custom_icon_labeled_checkbox.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_app_bar_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_input_field_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_date_drop_down_field_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/default_field/default_label_field_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/extension/handle_drag_email_tag_between_field_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/email_recovery_form_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/check_box_has_attachment_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/date_selection_field/date_selection_field_mobile_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/limits_banner.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/limits_banner.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/list_button_widget.dart';
|
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/list_button_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_suggestion_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class EmailRecoveryFormMobileBuilder extends GetWidget<EmailRecoveryController> {
|
class EmailRecoveryFormMobileBuilder extends StatelessWidget {
|
||||||
@override
|
|
||||||
final controller = Get.find<EmailRecoveryController>();
|
final EmailRecoveryController controller;
|
||||||
|
|
||||||
EmailRecoveryFormMobileBuilder({super.key});
|
const EmailRecoveryFormMobileBuilder(this.controller, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
final appLocalizations = AppLocalizations.of(context);
|
||||||
children: [
|
return Scaffold(
|
||||||
Column(
|
backgroundColor: Colors.white,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
body: GestureDetector(
|
||||||
children: [
|
onTap: FocusScope.of(context).unfocus,
|
||||||
Container(
|
child: SafeArea(
|
||||||
padding: EmailRecoveryFormStyles.padding,
|
child: ColoredBox(
|
||||||
alignment: Alignment.center,
|
color: Colors.white,
|
||||||
child: Text(
|
child: FocusTraversalGroup(
|
||||||
AppLocalizations.of(context).recoverDeletedMessages,
|
child: Column(
|
||||||
style: EmailRecoveryFormStyles.titleTextStyle,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
children: [
|
||||||
),
|
DefaultAppBarWidget(
|
||||||
Expanded(
|
title: appLocalizations.recoverDeletedMessages,
|
||||||
child: SingleChildScrollView(
|
imagePaths: controller.imagePaths,
|
||||||
physics: const ClampingScrollPhysics(),
|
responsiveUtils: controller.responsiveUtils,
|
||||||
child: Padding(
|
onBackAction: () => controller.closeView(context),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
LimitsBanner(
|
|
||||||
bannerContent: AppLocalizations.of(context).recoverDeletedMessagesBannerContent(controller.getRestorationHorizonAsString()),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Obx(() => DateSelectionFieldMobileWidget(
|
|
||||||
field: EmailRecoveryField.deletionDate,
|
|
||||||
recoveryTimeSelected: controller.deletionDateFieldSelected.value,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
onTap: () => controller.onSelectDeletionDateRange(context),
|
|
||||||
startDate: controller.startDeletionDate.value,
|
|
||||||
endDate: controller.endDeletionDate.value,
|
|
||||||
)),
|
|
||||||
Obx(() => DateSelectionFieldMobileWidget(
|
|
||||||
field: EmailRecoveryField.receptionDate,
|
|
||||||
recoveryTimeSelected: controller.receptionDateFieldSelected.value,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
onTap: () => controller.onSelectReceptionDateRange(context),
|
|
||||||
startDate: controller.startReceptionDate.value,
|
|
||||||
endDate: controller.endReceptionDate.value,
|
|
||||||
)),
|
|
||||||
TextInputFieldWidget(
|
|
||||||
field: EmailRecoveryField.subject,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
textEditingController: controller.subjectFieldInputController,
|
|
||||||
currentFocusNode: controller.focusManager.subjectFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.recipientsFieldFocusNode,
|
|
||||||
),
|
|
||||||
Obx(() => TextInputFieldSuggestionWidget(
|
|
||||||
keyTagEditor: controller.recipientsFieldKey,
|
|
||||||
field: EmailRecoveryField.recipients,
|
|
||||||
listEmailAddress: controller.listRecipients,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
focusNode: controller.focusManager.recipientsFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.senderFieldFocusNode,
|
|
||||||
expandMode: controller.recipientsExpandMode.value,
|
|
||||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
|
||||||
textEditingController: controller.recipientsFieldInputController,
|
|
||||||
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
|
||||||
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
|
||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
|
||||||
)),
|
|
||||||
Obx(() => TextInputFieldSuggestionWidget(
|
|
||||||
keyTagEditor: controller.senderFieldKey,
|
|
||||||
field: EmailRecoveryField.sender,
|
|
||||||
listEmailAddress: controller.listSenders,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
focusNode: controller.focusManager.senderFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
|
||||||
expandMode: controller.senderExpandMode.value,
|
|
||||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
|
||||||
textEditingController: controller.senderFieldInputController,
|
|
||||||
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
|
||||||
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
|
||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
|
||||||
)),
|
|
||||||
Obx(() => CheckBoxHasAttachmentWidget(
|
|
||||||
hasAttachmentValue: controller.hasAttachment.value,
|
|
||||||
currentFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
|
||||||
onChanged: (value) => controller.onChangeHasAttachment(value),
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
LimitsBanner(
|
||||||
|
bannerContent: appLocalizations
|
||||||
|
.dialogRecoverDeletedMessagesDescription(
|
||||||
|
controller.getRestorationHorizonAsString(),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15.0),
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.deletionDate.getTitle(appLocalizations),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
),
|
||||||
|
Obx(() => DefaultDateDropDownFieldWidget(
|
||||||
|
imagePaths: controller.imagePaths,
|
||||||
|
startDate: controller.startDeletionDate.value,
|
||||||
|
endDate: controller.endDeletionDate.value,
|
||||||
|
onReceiveTimeSelected: (receiveTimeType) {
|
||||||
|
controller.onDeletionDateTypeSelected(
|
||||||
|
context,
|
||||||
|
receiveTimeType,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onOpenDatPicker: () =>
|
||||||
|
controller.onSelectDeletionDateRange(context),
|
||||||
|
receiveTimeTypes: controller.deletionDateTimeTypes,
|
||||||
|
receiveTimeTypeSelected:
|
||||||
|
controller.deletionDateFieldSelected.value,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 15.0),
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.receptionDate.getTitle(appLocalizations),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
),
|
||||||
|
Obx(() => DefaultDateDropDownFieldWidget(
|
||||||
|
imagePaths: controller.imagePaths,
|
||||||
|
startDate: controller.startReceptionDate.value,
|
||||||
|
endDate: controller.endReceptionDate.value,
|
||||||
|
onReceiveTimeSelected: (receiveTimeType) {
|
||||||
|
controller.onReceptionDateTypeSelected(
|
||||||
|
context,
|
||||||
|
receiveTimeType,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onOpenDatPicker: () =>
|
||||||
|
controller.onSelectReceptionDateRange(context),
|
||||||
|
receiveTimeTypes: controller.receptionDateTimeTypes,
|
||||||
|
receiveTimeTypeSelected:
|
||||||
|
controller.receptionDateFieldSelected.value,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 15.0),
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.subject.getTitle(appLocalizations),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
),
|
||||||
|
DefaultInputFieldWithTabKeyWidget(
|
||||||
|
textEditingController: controller.subjectFieldInputController,
|
||||||
|
currentFocusNode: controller.focusManager.subjectFieldFocusNode,
|
||||||
|
nextFocusNode: controller.focusManager.recipientsFieldFocusNode,
|
||||||
|
hintText: FilterField.recoverSubject.getHintText(appLocalizations),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15.0),
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.recipients.getTitle(appLocalizations),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
),
|
||||||
|
Obx(() => DefaultAutocompleteInputFieldWidget(
|
||||||
|
field: FilterField.recipients,
|
||||||
|
listEmailAddress: controller.listRecipients,
|
||||||
|
expandMode: controller.recipientsExpandMode.value,
|
||||||
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
|
controller: controller.recipientsFieldInputController,
|
||||||
|
focusNode: controller.focusManager.recipientsFieldFocusNode,
|
||||||
|
nextFocusNode: controller.focusManager.senderFieldFocusNode,
|
||||||
|
keyTagEditor: controller.recipientsFieldKey,
|
||||||
|
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
||||||
|
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
||||||
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 15.0),
|
||||||
|
DefaultLabelFieldWidget(
|
||||||
|
label: FilterField.sender.getTitle(appLocalizations),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
),
|
||||||
|
Obx(() => DefaultAutocompleteInputFieldWidget(
|
||||||
|
field: FilterField.sender,
|
||||||
|
listEmailAddress: controller.listSenders,
|
||||||
|
expandMode: controller.senderExpandMode.value,
|
||||||
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
|
controller: controller.senderFieldInputController,
|
||||||
|
focusNode: controller.focusManager.senderFieldFocusNode,
|
||||||
|
nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
||||||
|
keyTagEditor: controller.senderFieldKey,
|
||||||
|
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
||||||
|
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
||||||
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 15.0),
|
||||||
|
Obx(() => CustomIconLabeledCheckbox(
|
||||||
|
label: AppLocalizations.of(context).hasAttachment,
|
||||||
|
svgIconPath: controller.imagePaths.icCheckboxUnselected,
|
||||||
|
selectedSvgIconPath: controller.imagePaths.icCheckboxSelected,
|
||||||
|
focusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
||||||
|
value: controller.hasAttachment.value,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
|
onChanged: controller.onChangeHasAttachment,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListButtonWidget(
|
||||||
|
onCancel: () => controller.closeView(context),
|
||||||
|
onRestore: () => controller.onRestore(context),
|
||||||
|
nextFocusNode: controller.focusManager.subjectFieldFocusNode,
|
||||||
|
isMobile: true,
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 44,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: EmailRecoveryFormStyles.bottomAreaPadding,
|
|
||||||
child: ListButtonWidget(
|
|
||||||
onCancel: () => controller.closeView(context),
|
|
||||||
onRestore: () => controller.onRestore(context),
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: EmailRecoveryFormStyles.iconClosePositionTop,
|
|
||||||
left: EmailRecoveryFormStyles.iconClosePositionRight,
|
|
||||||
child: buildIconWeb(
|
|
||||||
icon: SvgPicture.asset(
|
|
||||||
controller.imagePaths.icClose,
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
tooltip: AppLocalizations.of(context).close,
|
|
||||||
onTap: () => controller.closeView(context),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-149
@@ -1,149 +0,0 @@
|
|||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/email_recovery_controller.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/email_recovery_form_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/check_box_has_attachment_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/date_selection_field/date_selection_field_web_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/limits_banner.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/list_button_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_suggestion_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|
||||||
|
|
||||||
class EmailRecoveryFormTabletBuilder extends GetWidget<EmailRecoveryController> {
|
|
||||||
@override
|
|
||||||
final controller = Get.find<EmailRecoveryController>();
|
|
||||||
|
|
||||||
EmailRecoveryFormTabletBuilder({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: EmailRecoveryFormStyles.padding,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context).recoverDeletedMessages,
|
|
||||||
style: EmailRecoveryFormStyles.titleTextStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
physics: const ClampingScrollPhysics(),
|
|
||||||
child: Padding(
|
|
||||||
padding: EmailRecoveryFormStyles.inputAreaPadding,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
LimitsBanner(
|
|
||||||
bannerContent: AppLocalizations.of(context).recoverDeletedMessagesBannerContent(controller.getRestorationHorizonAsString()),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Obx(() => DateSelectionFieldWebWidget(
|
|
||||||
field: EmailRecoveryField.deletionDate,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
startDate: controller.startDeletionDate.value,
|
|
||||||
endDate: controller.endDeletionDate.value,
|
|
||||||
recoveryTimeSelected: controller.deletionDateFieldSelected.value,
|
|
||||||
onTapCalendar: () => controller.onSelectDeletionDateRange(context),
|
|
||||||
onRecoveryTimeSelected: (type) => controller.onDeletionDateTypeSelected(context, type),
|
|
||||||
restorationHorizon: controller.getRestorationHorizonAsDateTime(),
|
|
||||||
)),
|
|
||||||
Obx(() => DateSelectionFieldWebWidget(
|
|
||||||
field: EmailRecoveryField.receptionDate,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
startDate: controller.startReceptionDate.value,
|
|
||||||
endDate: controller.endReceptionDate.value,
|
|
||||||
recoveryTimeSelected: controller.receptionDateFieldSelected.value,
|
|
||||||
onTapCalendar: () => controller.onSelectReceptionDateRange(context),
|
|
||||||
onRecoveryTimeSelected: (type) => controller.onReceptionDateTypeSelected(context, type),
|
|
||||||
restorationHorizon: controller.getRestorationHorizonAsDateTime(),
|
|
||||||
)),
|
|
||||||
TextInputFieldWidget(
|
|
||||||
field: EmailRecoveryField.subject,
|
|
||||||
textEditingController: controller.subjectFieldInputController,
|
|
||||||
currentFocusNode: controller.focusManager.subjectFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.recipientsFieldFocusNode,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
),
|
|
||||||
Obx(() => TextInputFieldSuggestionWidget(
|
|
||||||
keyTagEditor: controller.recipientsFieldKey,
|
|
||||||
field: EmailRecoveryField.recipients,
|
|
||||||
listEmailAddress: controller.listRecipients,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
expandMode: controller.recipientsExpandMode.value,
|
|
||||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
|
||||||
textEditingController: controller.recipientsFieldInputController,
|
|
||||||
focusNode: controller.focusManager.recipientsFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.senderFieldFocusNode,
|
|
||||||
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
|
||||||
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
|
||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
|
||||||
)),
|
|
||||||
Obx(() => TextInputFieldSuggestionWidget(
|
|
||||||
keyTagEditor: controller.senderFieldKey,
|
|
||||||
field: EmailRecoveryField.sender,
|
|
||||||
listEmailAddress: controller.listSenders,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
expandMode: controller.senderExpandMode.value,
|
|
||||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
|
||||||
textEditingController: controller.senderFieldInputController,
|
|
||||||
focusNode: controller.focusManager.senderFieldFocusNode,
|
|
||||||
nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
|
||||||
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
|
|
||||||
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
|
|
||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EmailRecoveryFormStyles.bottomAreaPadding,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Obx(() => CheckBoxHasAttachmentWidget(
|
|
||||||
hasAttachmentValue: controller.hasAttachment.value,
|
|
||||||
currentFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
|
|
||||||
onChanged: (value) => controller.onChangeHasAttachment(value),
|
|
||||||
)),
|
|
||||||
SizedBox(width: controller.responsiveUtils.getDeviceWidth(context) * 0.04),
|
|
||||||
Expanded(
|
|
||||||
child: ListButtonWidget(
|
|
||||||
onCancel: () => controller.closeView(context),
|
|
||||||
onRestore: () => controller.onRestore(context),
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: EmailRecoveryFormStyles.iconClosePositionTop,
|
|
||||||
right: EmailRecoveryFormStyles.iconClosePositionRight,
|
|
||||||
child: buildIconWeb(
|
|
||||||
icon: SvgPicture.asset(
|
|
||||||
controller.imagePaths.icClose,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
tooltip: AppLocalizations.of(context).close,
|
|
||||||
onTap: () => controller.closeView(context),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,32 +3,28 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class LimitsBanner extends StatelessWidget {
|
class LimitsBanner extends StatelessWidget {
|
||||||
final String bannerContent;
|
final String bannerContent;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
final bool isCenter;
|
||||||
|
|
||||||
const LimitsBanner({
|
const LimitsBanner({
|
||||||
super.key,
|
super.key,
|
||||||
required this.bannerContent,
|
required this.bannerContent,
|
||||||
|
this.isCenter = false,
|
||||||
|
this.padding,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
final bodyWidget = Text(
|
||||||
width: double.infinity,
|
bannerContent,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
|
textAlign: isCenter ? TextAlign.center : TextAlign.start,
|
||||||
decoration: BoxDecoration(
|
style: ThemeUtils.textStyleM3BodyMedium1,
|
||||||
color: Colors.yellow[400],
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
bannerContent,
|
|
||||||
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (padding != null) {
|
||||||
|
return Padding(padding: padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,47 +1,85 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/list_button_widget_styles.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_action_button_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class ListButtonWidget extends StatelessWidget {
|
class ListButtonWidget extends StatelessWidget {
|
||||||
final VoidCallback onCancel;
|
final VoidCallback onCancel;
|
||||||
final VoidCallback onRestore;
|
final VoidCallback onRestore;
|
||||||
final ResponsiveUtils responsiveUtils;
|
final bool isMobile;
|
||||||
|
final FocusNode? nextFocusNode;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
const ListButtonWidget({
|
const ListButtonWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.onCancel,
|
required this.onCancel,
|
||||||
required this.onRestore,
|
required this.onRestore,
|
||||||
required this.responsiveUtils,
|
this.isMobile = false,
|
||||||
|
this.nextFocusNode,
|
||||||
|
this.padding,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
Widget cancelButton = Container(
|
||||||
children: [
|
constraints: !isMobile ? const BoxConstraints(minWidth: 67) : null,
|
||||||
Expanded(
|
height: 48,
|
||||||
child: TextActionButtonWidget(
|
child: ConfirmDialogButton(
|
||||||
colorButton: AppColor.colorBgSearchBar,
|
label: AppLocalizations.of(context).cancel,
|
||||||
colorText: AppColor.primaryColor,
|
onTapAction: onCancel,
|
||||||
text: AppLocalizations.of(context).cancel,
|
),
|
||||||
onAction: onCancel
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: responsiveUtils.isMobile(context)
|
|
||||||
? ListButtonWidgetStyles.mobileSpace
|
|
||||||
: ListButtonWidgetStyles.space
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TextActionButtonWidget(
|
|
||||||
colorButton: AppColor.primaryColor,
|
|
||||||
colorText: Colors.white,
|
|
||||||
text: AppLocalizations.of(context).restore,
|
|
||||||
onAction: onRestore
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget restoreButton = Container(
|
||||||
|
constraints: !isMobile ? const BoxConstraints(minWidth: 117) : null,
|
||||||
|
height: 48,
|
||||||
|
child: ConfirmDialogButton(
|
||||||
|
label: AppLocalizations.of(context).restore,
|
||||||
|
backgroundColor: AppColor.primaryMain,
|
||||||
|
textColor: Colors.white,
|
||||||
|
onTapAction: onRestore,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nextFocusNode != null) {
|
||||||
|
restoreButton = KeyboardListener(
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
onKeyEvent: (event) {
|
||||||
|
if (event is KeyDownEvent &&
|
||||||
|
event.logicalKey == LogicalKeyboardKey.tab) {
|
||||||
|
nextFocusNode?.requestFocus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: restoreButton,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget bodyWidget;
|
||||||
|
|
||||||
|
if (!isMobile) {
|
||||||
|
bodyWidget = Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Flexible(child: cancelButton),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Flexible(child: restoreButton),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
bodyWidget = Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: cancelButton),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(child: restoreButton),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (padding != null) {
|
||||||
|
return Padding(padding: padding!, child: bodyWidget);
|
||||||
|
} else {
|
||||||
|
return bodyWidget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import 'package:core/presentation/utils/theme_utils.dart';
|
|
||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/text_action_button_widget_styles.dart';
|
|
||||||
|
|
||||||
class TextActionButtonWidget extends StatelessWidget {
|
|
||||||
final Color colorButton;
|
|
||||||
final Color colorText;
|
|
||||||
final String text;
|
|
||||||
final VoidCallback onAction;
|
|
||||||
final double? minWidth;
|
|
||||||
|
|
||||||
const TextActionButtonWidget({
|
|
||||||
super.key,
|
|
||||||
required this.colorButton,
|
|
||||||
required this.colorText,
|
|
||||||
required this.text,
|
|
||||||
required this.onAction,
|
|
||||||
this.minWidth,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return buildButtonWrapText(
|
|
||||||
text,
|
|
||||||
radius: TextActionButtonWidgetStyles.radius,
|
|
||||||
height: TextActionButtonWidgetStyles.height,
|
|
||||||
minWidth: minWidth,
|
|
||||||
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
||||||
fontSize: TextActionButtonWidgetStyles.fontSize,
|
|
||||||
color: colorText,
|
|
||||||
fontWeight: FontWeight.w500
|
|
||||||
),
|
|
||||||
bgColor: colorButton,
|
|
||||||
onTap: onAction
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:core/presentation/extensions/string_extension.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
|
||||||
import 'package:model/model.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/text_input_field/avatar_suggestion_item_widget_styles.dart';
|
|
||||||
|
|
||||||
class AvatarSuggestionItemWidget extends StatelessWidget {
|
|
||||||
final EmailAddress emailAddress;
|
|
||||||
|
|
||||||
const AvatarSuggestionItemWidget({super.key, required this.emailAddress});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: AvatarSuggestionItemWidgetStyles.width,
|
|
||||||
height: AvatarSuggestionItemWidgetStyles.height,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: AppColor.avatarColor,
|
|
||||||
border: Border.all(
|
|
||||||
color: AppColor.colorShadowBgContentEmail,
|
|
||||||
width: AvatarSuggestionItemWidgetStyles.borderWidth
|
|
||||||
)
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
emailAddress.asString().isNotEmpty
|
|
||||||
? emailAddress.asString().firstLetterToUpperCase
|
|
||||||
: '',
|
|
||||||
style: AvatarSuggestionItemWidgetStyles.textStyle,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/text_input_field/avatar_tag_item_widget_styles.dart';
|
|
||||||
|
|
||||||
class AvatarTagItemWidget extends StatelessWidget {
|
|
||||||
final String tagName;
|
|
||||||
|
|
||||||
const AvatarTagItemWidget({super.key, required this.tagName});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: AvatarTagItemWidgetStyles.size,
|
|
||||||
height: AvatarTagItemWidgetStyles.size,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter,
|
|
||||||
colors:AppColor.mapGradientColor[_generateIndex(tagName)]
|
|
||||||
),
|
|
||||||
border: Border.all(
|
|
||||||
color: AppColor.colorShadowBgContentEmail,
|
|
||||||
width: AvatarTagItemWidgetStyles.borderWidth
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
tagName.isNotEmpty ? tagName[0].toUpperCase() : '',
|
|
||||||
style: AvatarTagItemWidgetStyles.textStyle
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _generateIndex(String tag) {
|
|
||||||
if (tag.isNotEmpty) {
|
|
||||||
final codeUnits = tag.codeUnits;
|
|
||||||
if (codeUnits.isNotEmpty) {
|
|
||||||
final sumCodeUnits = codeUnits.sum;
|
|
||||||
final index = sumCodeUnits % AppColor.mapGradientColor.length;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-122
@@ -1,122 +0,0 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:core/presentation/utils/style_utils.dart';
|
|
||||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
|
||||||
import 'package:core/utils/direction_utils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
|
||||||
import 'package:model/model.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/text_input_field/suggestion_tag_item_widget_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/avatar_tag_item_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/text_input_field_suggestion_widget.dart';
|
|
||||||
|
|
||||||
class SuggestionTagItemWidget extends StatelessWidget {
|
|
||||||
final bool isCollapsed;
|
|
||||||
final bool isLatestTagFocused;
|
|
||||||
final bool isLatestEmail;
|
|
||||||
final EmailRecoveryField field;
|
|
||||||
final EmailAddress currentEmailAddress;
|
|
||||||
final List<EmailAddress> currentListEmailAddress;
|
|
||||||
final List<EmailAddress> collapsedListEmailAddress;
|
|
||||||
final OnDeleteTagAction? onDeleteTagAction;
|
|
||||||
final OnShowFullListEmailAddressAction? onShowFullAction;
|
|
||||||
|
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
|
||||||
|
|
||||||
SuggestionTagItemWidget({
|
|
||||||
super.key,
|
|
||||||
required this.field,
|
|
||||||
required this.currentEmailAddress,
|
|
||||||
required this.currentListEmailAddress,
|
|
||||||
required this.collapsedListEmailAddress,
|
|
||||||
this.isCollapsed = false,
|
|
||||||
this.isLatestTagFocused = false,
|
|
||||||
this.isLatestEmail = false,
|
|
||||||
this.onDeleteTagAction,
|
|
||||||
this.onShowFullAction,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Stack(
|
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.only(
|
|
||||||
top: SuggestionTagItemWidgetStyles.paddingTop,
|
|
||||||
end: isCollapsed
|
|
||||||
? SuggestionTagItemWidgetStyles.paddingEndCollapsed
|
|
||||||
: SuggestionTagItemWidgetStyles.paddingEndExpanded,
|
|
||||||
),
|
|
||||||
child: TextFieldTapRegion(
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () => isCollapsed
|
|
||||||
? onShowFullAction?.call(field)
|
|
||||||
: null,
|
|
||||||
child: MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.grab,
|
|
||||||
child: Chip(
|
|
||||||
labelPadding: EdgeInsetsDirectional.symmetric(
|
|
||||||
horizontal: SuggestionTagItemWidgetStyles.labelPaddingHorizontal,
|
|
||||||
vertical: DirectionUtils.isDirectionRTLByHasAnyRtl(currentEmailAddress.asString())
|
|
||||||
? 0
|
|
||||||
: SuggestionTagItemWidgetStyles.labelPaddingVertical,
|
|
||||||
),
|
|
||||||
label: Text(
|
|
||||||
currentEmailAddress.asString(),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: CommonTextStyle.defaultTextOverFlow,
|
|
||||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
|
||||||
),
|
|
||||||
deleteIcon: SvgPicture.asset(
|
|
||||||
_imagePaths.icClose,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
labelStyle: SuggestionTagItemWidgetStyles.labelStyle,
|
|
||||||
backgroundColor: _getTagBackgroundColor(),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: SuggestionTagItemWidgetStyles.borderRadius,
|
|
||||||
side: _getTagBorderSide(),
|
|
||||||
),
|
|
||||||
avatar: currentEmailAddress.emailAddress.isNotEmpty
|
|
||||||
? AvatarTagItemWidget(tagName: currentEmailAddress.emailAddress)
|
|
||||||
: null,
|
|
||||||
onDeleted: () => onDeleteTagAction?.call(currentEmailAddress),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isCollapsed)
|
|
||||||
TMailButtonWidget.fromText(
|
|
||||||
margin: SuggestionTagItemWidgetStyles.collapsedTextMargin,
|
|
||||||
text: '+${currentListEmailAddress.length - collapsedListEmailAddress.length}',
|
|
||||||
onTapActionCallback: () => onShowFullAction?.call(field),
|
|
||||||
borderRadius: SuggestionTagItemWidgetStyles.collapsedTextBorderRadius,
|
|
||||||
textStyle: SuggestionTagItemWidgetStyles.labelStyle,
|
|
||||||
padding: SuggestionTagItemWidgetStyles.collapsedTextPadding,
|
|
||||||
backgroundColor: AppColor.colorEmailAddressTag,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Color _getTagBackgroundColor() {
|
|
||||||
if (isLatestTagFocused && isLatestEmail) {
|
|
||||||
return AppColor.colorItemRecipientSelected;
|
|
||||||
} else {
|
|
||||||
return AppColor.colorEmailAddressTag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BorderSide _getTagBorderSide() {
|
|
||||||
if (isLatestTagFocused && isLatestEmail) {
|
|
||||||
return SuggestionTagItemWidgetStyles.latestTagBorderSide;
|
|
||||||
} else {
|
|
||||||
return BorderSide.none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-380
@@ -1,380 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
|
||||||
import 'package:model/mailbox/expand_mode.dart';
|
|
||||||
import 'package:model/extensions/email_address_extension.dart';
|
|
||||||
import 'package:super_tag_editor/tag_editor.dart';
|
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
|
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_suggestion_item_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/text_input_field/text_input_suggestion_field_widget_styles.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/widgets/text_input_field/suggestion_tag_item_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
|
||||||
|
|
||||||
typedef OnSuggestionEmailAddress = Future<List<EmailAddress>> Function(String word, {int? limit});
|
|
||||||
typedef OnUpdateListEmailAddressAction = void Function(EmailRecoveryField field, List<EmailAddress> newDate);
|
|
||||||
typedef OnDeleteEmailAddressTypeAction = void Function(EmailRecoveryField field, EmailAddress emailAddress);
|
|
||||||
typedef OnShowFullListEmailAddressAction = void Function(EmailRecoveryField field);
|
|
||||||
typedef OnDeleteTagAction = void Function(EmailAddress emailAddress);
|
|
||||||
|
|
||||||
class TextInputFieldSuggestionWidget extends StatefulWidget {
|
|
||||||
final EmailRecoveryField field;
|
|
||||||
final List<EmailAddress> listEmailAddress;
|
|
||||||
final ExpandMode expandMode;
|
|
||||||
final ResponsiveUtils responsiveUtils;
|
|
||||||
final FocusNode? focusNode;
|
|
||||||
final GlobalKey? keyTagEditor;
|
|
||||||
final FocusNode? nextFocusNode;
|
|
||||||
final EdgeInsetsGeometry? padding;
|
|
||||||
final EdgeInsetsGeometry? margin;
|
|
||||||
final OnSuggestionEmailAddress? onSuggestionEmailAddress;
|
|
||||||
final OnUpdateListEmailAddressAction? onUpdateListEmailAddressAction;
|
|
||||||
final OnDeleteEmailAddressTypeAction? onDeleteEmailAddressTypeAction;
|
|
||||||
final OnShowFullListEmailAddressAction? onShowFullListEmailAddressAction;
|
|
||||||
final OnDeleteTagAction? onDeleteTagAction;
|
|
||||||
final TextEditingController? textEditingController;
|
|
||||||
final VoidCallback? onCreateAction;
|
|
||||||
final int minInputLengthAutocomplete;
|
|
||||||
|
|
||||||
const TextInputFieldSuggestionWidget({
|
|
||||||
super.key,
|
|
||||||
required this.field,
|
|
||||||
required this.listEmailAddress,
|
|
||||||
required this.responsiveUtils,
|
|
||||||
this.expandMode = ExpandMode.EXPAND,
|
|
||||||
this.minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete,
|
|
||||||
this.focusNode,
|
|
||||||
this.keyTagEditor,
|
|
||||||
this.nextFocusNode,
|
|
||||||
this.padding,
|
|
||||||
this.margin,
|
|
||||||
this.onSuggestionEmailAddress,
|
|
||||||
this.onUpdateListEmailAddressAction,
|
|
||||||
this.onDeleteEmailAddressTypeAction,
|
|
||||||
this.onShowFullListEmailAddressAction,
|
|
||||||
this.onDeleteTagAction,
|
|
||||||
this.textEditingController,
|
|
||||||
this.onCreateAction,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<TextInputFieldSuggestionWidget> createState() => _TextInputFieldSuggestionWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TextInputFieldSuggestionWidgetState extends State<TextInputFieldSuggestionWidget> {
|
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
|
||||||
|
|
||||||
bool _lastTagFocused = false;
|
|
||||||
late List<EmailAddress> _currentListEmailAddress;
|
|
||||||
Timer? _gapBetweenTagChangedAndFindSuggestion;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_currentListEmailAddress = widget.listEmailAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant TextInputFieldSuggestionWidget oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (widget.listEmailAddress != oldWidget.listEmailAddress) {
|
|
||||||
_currentListEmailAddress = widget.listEmailAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: TextInputSuggestionFieldWidgetStyles.padding,
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
final title = SizedBox(
|
|
||||||
width: TextInputSuggestionFieldWidgetStyles.titleWidth,
|
|
||||||
child: Text(
|
|
||||||
widget.field.getTitle(context),
|
|
||||||
style: widget.field.getTitleTextStyle(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final inputField = StatefulBuilder(
|
|
||||||
builder: (context, setState) {
|
|
||||||
return TagEditor<SuggestionEmailAddress>(
|
|
||||||
key: widget.keyTagEditor,
|
|
||||||
length: _collapsedListEmailAddress.length,
|
|
||||||
controller: widget.textEditingController,
|
|
||||||
focusNodeKeyboard: widget.focusNode,
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
textInputAction: TextInputAction.done,
|
|
||||||
debounceDuration: const Duration(milliseconds: 150),
|
|
||||||
enabled: widget.field == EmailRecoveryField.sender && _currentListEmailAddress.length == 1
|
|
||||||
? false
|
|
||||||
: true,
|
|
||||||
inputDecoration: InputDecoration(
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.white,
|
|
||||||
border: TextInputSuggestionFieldWidgetStyles.border,
|
|
||||||
hintText: widget.field.getHintText(context),
|
|
||||||
hintStyle: widget.field.getHintTextStyle(),
|
|
||||||
isDense: true,
|
|
||||||
contentPadding: _currentListEmailAddress.isNotEmpty
|
|
||||||
? TextInputSuggestionFieldWidgetStyles.contentPadding
|
|
||||||
: TextInputSuggestionFieldWidgetStyles.emptyListContentPadding,
|
|
||||||
),
|
|
||||||
padding: _currentListEmailAddress.isNotEmpty
|
|
||||||
? TextInputSuggestionFieldWidgetStyles.inputFieldPadding
|
|
||||||
: EdgeInsets.zero,
|
|
||||||
borderRadius: TextInputSuggestionFieldWidgetStyles.borderRadius,
|
|
||||||
borderSize: TextInputSuggestionFieldWidgetStyles.borderSize,
|
|
||||||
focusedBorderColor: AppColor.primaryColor,
|
|
||||||
enableBorder: true,
|
|
||||||
enableBorderColor: AppColor.colorInputBorderCreateMailbox,
|
|
||||||
minTextFieldWidth: TextInputSuggestionFieldWidgetStyles.minTextFieldWidth,
|
|
||||||
resetTextOnSubmitted: true,
|
|
||||||
autoScrollToInput: false,
|
|
||||||
suggestionsBoxElevation: TextInputSuggestionFieldWidgetStyles.suggestionsBoxElevation,
|
|
||||||
suggestionsBoxBackgroundColor: Colors.white,
|
|
||||||
suggestionsBoxRadius: TextInputSuggestionFieldWidgetStyles.suggestionsBoxRadius,
|
|
||||||
suggestionsBoxMaxHeight: TextInputSuggestionFieldWidgetStyles.suggestionsBoxMaxHeight,
|
|
||||||
suggestionItemHeight: TextInputSuggestionFieldWidgetStyles.suggestionBoxItemHeight,
|
|
||||||
suggestionBoxWidth: _getSuggestionBoxWidth(constraints.maxWidth),
|
|
||||||
textStyle: TextInputSuggestionFieldWidgetStyles.inputFieldTextStyle,
|
|
||||||
onFocusTagAction: (focused) => _handleFocusTagAction.call(focused, setState),
|
|
||||||
onDeleteTagAction: () => _handleDeleteLatestTagAction.call(setState),
|
|
||||||
onSelectOptionAction: (item) => _handleSelectOptionAction.call(item, setState),
|
|
||||||
onSubmitted: (value) => _handleSubmitTagAction.call(value, setState),
|
|
||||||
tagBuilder: (context, index) {
|
|
||||||
final currentEmailAddress = _currentListEmailAddress.elementAt(index);
|
|
||||||
final isLatestEmail = currentEmailAddress == _currentListEmailAddress.last;
|
|
||||||
return SuggestionTagItemWidget(
|
|
||||||
field: widget.field,
|
|
||||||
currentEmailAddress: currentEmailAddress,
|
|
||||||
currentListEmailAddress: _currentListEmailAddress,
|
|
||||||
collapsedListEmailAddress: _collapsedListEmailAddress,
|
|
||||||
isLatestEmail: isLatestEmail,
|
|
||||||
isCollapsed: _isCollapse,
|
|
||||||
isLatestTagFocused: _lastTagFocused,
|
|
||||||
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, setState),
|
|
||||||
onShowFullAction: widget.onShowFullListEmailAddressAction,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onTagChanged: (tag) => _handleOnTagChangeAction.call(tag, setState),
|
|
||||||
findSuggestions: (queryString) => _findSuggestions(
|
|
||||||
queryString,
|
|
||||||
limit: AppConfig.defaultLimitAutocomplete,
|
|
||||||
),
|
|
||||||
isLoadMoreOnlyOnce: true,
|
|
||||||
isLoadMoreReplaceAllOld: false,
|
|
||||||
loadMoreSuggestions: _findSuggestions,
|
|
||||||
suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) {
|
|
||||||
return RecipientSuggestionItemWidget(
|
|
||||||
imagePaths: _imagePaths,
|
|
||||||
suggestionState: suggestionEmailAddress.state,
|
|
||||||
emailAddress: suggestionEmailAddress.emailAddress,
|
|
||||||
suggestionValid: suggestionValid,
|
|
||||||
highlight: highlight,
|
|
||||||
onSelectedAction: (emailAddress) {
|
|
||||||
setState(() => _currentListEmailAddress.add(emailAddress));
|
|
||||||
_updateListEmailAddressAction();
|
|
||||||
tagEditorState.resetTextField();
|
|
||||||
tagEditorState.closeSuggestionBox();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onHandleKeyEventAction: (event) {
|
|
||||||
if (event is KeyDownEvent) {
|
|
||||||
switch (event.logicalKey) {
|
|
||||||
case LogicalKeyboardKey.tab:
|
|
||||||
widget.nextFocusNode?.requestFocus();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (widget.responsiveUtils.isMobile(context)) {
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
title,
|
|
||||||
const SizedBox(height: TextInputSuggestionFieldWidgetStyles.spaceMobile),
|
|
||||||
inputField,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
title,
|
|
||||||
const SizedBox(width: TextInputSuggestionFieldWidgetStyles.space),
|
|
||||||
Expanded(
|
|
||||||
child: inputField,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _isCollapse => _currentListEmailAddress.length > 1 && widget.expandMode == ExpandMode.COLLAPSE;
|
|
||||||
|
|
||||||
List<EmailAddress> get _collapsedListEmailAddress => _isCollapse
|
|
||||||
? _currentListEmailAddress.sublist(0, 1)
|
|
||||||
: _currentListEmailAddress;
|
|
||||||
|
|
||||||
double? _getSuggestionBoxWidth(double maxWidth) {
|
|
||||||
if (maxWidth < ResponsiveUtils.minTabletWidth) {
|
|
||||||
final newWidth = min(maxWidth, 300.0);
|
|
||||||
return newWidth;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleFocusTagAction(bool focused, StateSetter stateSetter) {
|
|
||||||
if (mounted) {
|
|
||||||
stateSetter(() => _lastTagFocused = focused);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleDeleteLatestTagAction(StateSetter stateSetter) {
|
|
||||||
if (_currentListEmailAddress.isNotEmpty) {
|
|
||||||
stateSetter(_currentListEmailAddress.removeLast);
|
|
||||||
_updateListEmailAddressAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateListEmailAddressAction() {
|
|
||||||
widget.onUpdateListEmailAddressAction?.call(
|
|
||||||
widget.field,
|
|
||||||
_currentListEmailAddress
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleSelectOptionAction(
|
|
||||||
SuggestionEmailAddress suggestionEmailAddress,
|
|
||||||
StateSetter stateSetter
|
|
||||||
) {
|
|
||||||
if (!_isDuplicated(suggestionEmailAddress.emailAddress.emailAddress)) {
|
|
||||||
stateSetter(() => _currentListEmailAddress.add(suggestionEmailAddress.emailAddress));
|
|
||||||
_updateListEmailAddressAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isDuplicated(String inputEmail) {
|
|
||||||
if (inputEmail.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return _currentListEmailAddress
|
|
||||||
.map((emailAddress) => emailAddress.email)
|
|
||||||
.whereNotNull()
|
|
||||||
.contains(inputEmail);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleSubmitTagAction(
|
|
||||||
String value,
|
|
||||||
StateSetter stateSetter
|
|
||||||
) {
|
|
||||||
final textTrim = value.trim();
|
|
||||||
if (textTrim.isEmpty) {
|
|
||||||
widget.onCreateAction?.call();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isDuplicated(textTrim)) {
|
|
||||||
stateSetter(() => _currentListEmailAddress.add(EmailAddress(null, textTrim)));
|
|
||||||
_updateListEmailAddressAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleDeleteTagAction(EmailAddress emailAddress, StateSetter stateSetter) {
|
|
||||||
if (_currentListEmailAddress.isNotEmpty) {
|
|
||||||
stateSetter(() => _currentListEmailAddress.remove(emailAddress));
|
|
||||||
_updateListEmailAddressAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleOnTagChangeAction(
|
|
||||||
String value,
|
|
||||||
StateSetter stateSetter
|
|
||||||
) {
|
|
||||||
final textTrim = value.trim();
|
|
||||||
if (!_isDuplicated(textTrim)) {
|
|
||||||
stateSetter(() => _currentListEmailAddress.add(EmailAddress(null, textTrim)));
|
|
||||||
_updateListEmailAddressAction();
|
|
||||||
}
|
|
||||||
_gapBetweenTagChangedAndFindSuggestion = Timer(
|
|
||||||
const Duration(seconds: 1),
|
|
||||||
_handleGapBetweenTagChangedAndFindSuggestion
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleGapBetweenTagChangedAndFindSuggestion() {
|
|
||||||
log('_TextFieldAutocompleteEmailAddressWebState::_handleGapBetweenTagChangedAndFindSuggestion:Timeout');
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureOr<List<SuggestionEmailAddress>> _findSuggestions(String query, {int? limit}) async {
|
|
||||||
if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
final processedQuery = query.trim();
|
|
||||||
if (processedQuery.isEmpty) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
final tmailSuggestion = List<SuggestionEmailAddress>.empty(growable: true);
|
|
||||||
if (
|
|
||||||
processedQuery.length >= widget.minInputLengthAutocomplete
|
|
||||||
&& widget.onSuggestionEmailAddress != null
|
|
||||||
) {
|
|
||||||
final listEmailAddress = await widget.onSuggestionEmailAddress!(
|
|
||||||
processedQuery,
|
|
||||||
limit: limit,
|
|
||||||
);
|
|
||||||
log('_TextFieldAutocompleteEmailAddressWebState::_findSuggestions: listEmailAddress: $listEmailAddress');
|
|
||||||
final listSuggestionEmailAddress = listEmailAddress.map((emailAddress) => _toSuggestionEmailAddress(emailAddress, _currentListEmailAddress));
|
|
||||||
tmailSuggestion.addAll(listSuggestionEmailAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmailSuggestion.addAll(_matchedSuggestionEmailAddress(processedQuery, _currentListEmailAddress));
|
|
||||||
|
|
||||||
final currentTextOnTextField = widget.textEditingController?.text ?? '';
|
|
||||||
if (currentTextOnTextField.isEmpty) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmailSuggestion.toSet().toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
SuggestionEmailAddress _toSuggestionEmailAddress(EmailAddress item, List<EmailAddress> addedEmailAddresses) {
|
|
||||||
if (addedEmailAddresses.contains(item)) {
|
|
||||||
return SuggestionEmailAddress(item, state: SuggestionEmailState.duplicated);
|
|
||||||
} else {
|
|
||||||
return SuggestionEmailAddress(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<SuggestionEmailAddress> _matchedSuggestionEmailAddress(String query, List<EmailAddress> addedEmailAddress) {
|
|
||||||
return addedEmailAddress
|
|
||||||
.where((addedMail) => addedMail.emailAddress.contains(query))
|
|
||||||
.map((emailAddress) => SuggestionEmailAddress(
|
|
||||||
emailAddress,
|
|
||||||
state: SuggestionEmailState.duplicated
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-102
@@ -1,102 +0,0 @@
|
|||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
|
||||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger_mobile.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_field.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email_recovery/presentation/styles/text_input_field/text_input_field_widget_styles.dart';
|
|
||||||
|
|
||||||
typedef OnTextSubmitted = void Function(String text)?;
|
|
||||||
|
|
||||||
class TextInputFieldWidget extends StatelessWidget {
|
|
||||||
final EmailRecoveryField field;
|
|
||||||
final ResponsiveUtils responsiveUtils;
|
|
||||||
final ImagePaths imagePaths;
|
|
||||||
final TextEditingController textEditingController;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final MouseCursor? mouseCursor;
|
|
||||||
final FocusNode? currentFocusNode;
|
|
||||||
final FocusNode? nextFocusNode;
|
|
||||||
final OnTextSubmitted? onTextSubmitted;
|
|
||||||
|
|
||||||
const TextInputFieldWidget({
|
|
||||||
super.key,
|
|
||||||
required this.field,
|
|
||||||
required this.responsiveUtils,
|
|
||||||
required this.imagePaths,
|
|
||||||
required this.textEditingController,
|
|
||||||
this.onTap,
|
|
||||||
this.mouseCursor,
|
|
||||||
this.currentFocusNode,
|
|
||||||
this.nextFocusNode,
|
|
||||||
this.onTextSubmitted,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final title = SizedBox(
|
|
||||||
width: responsiveUtils.isMobile(context)
|
|
||||||
? null
|
|
||||||
: TextInputFieldWidgetStyles.titleWidth,
|
|
||||||
child: Text(
|
|
||||||
field.getTitle(context),
|
|
||||||
style: field.getTitleTextStyle(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final inputField = TextFieldBuilder(
|
|
||||||
controller: textEditingController,
|
|
||||||
readOnly: false,
|
|
||||||
mouseCursor: mouseCursor,
|
|
||||||
maxLines: 1,
|
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
textStyle: TextInputFieldWidgetStyles.inputtedTextStyle,
|
|
||||||
onTap: onTap,
|
|
||||||
onTextSubmitted: onTextSubmitted,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.white,
|
|
||||||
contentPadding: TextInputFieldWidgetStyles.contentPadding,
|
|
||||||
enabledBorder: TextInputFieldWidgetStyles.enableBorder,
|
|
||||||
border: TextInputFieldWidgetStyles.border,
|
|
||||||
focusedBorder: TextInputFieldWidgetStyles.focusedBorder,
|
|
||||||
hintText: field.getHintText(context),
|
|
||||||
hintStyle: field.getHintTextStyle(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return KeyboardListener(
|
|
||||||
focusNode: currentFocusNode ?? FocusNode(),
|
|
||||||
onKeyEvent: (event) {
|
|
||||||
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.tab) {
|
|
||||||
nextFocusNode?.requestFocus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: TextInputFieldWidgetStyles.padding,
|
|
||||||
child: responsiveUtils.isMobile(context)
|
|
||||||
? Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
title,
|
|
||||||
const SizedBox(height: TextInputFieldWidgetStyles.spaceMobile),
|
|
||||||
inputField,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
height: 44,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
title,
|
|
||||||
const SizedBox(width: TextInputFieldWidgetStyles.space),
|
|
||||||
Expanded(
|
|
||||||
child: inputField,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-140
@@ -1,140 +0,0 @@
|
|||||||
import 'package:core/presentation/resources/image_paths.dart';
|
|
||||||
import 'package:core/presentation/utils/style_utils.dart';
|
|
||||||
import 'package:core/presentation/utils/theme_utils.dart';
|
|
||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
|
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart';
|
|
||||||
|
|
||||||
class DateDropDownButton extends StatelessWidget {
|
|
||||||
|
|
||||||
final ImagePaths imagePaths;
|
|
||||||
final DateTime? startDate;
|
|
||||||
final DateTime? endDate;
|
|
||||||
final EmailReceiveTimeType? receiveTimeSelected;
|
|
||||||
final Function(EmailReceiveTimeType)? onReceiveTimeSelected;
|
|
||||||
|
|
||||||
const DateDropDownButton(
|
|
||||||
this.imagePaths, {
|
|
||||||
Key? key,
|
|
||||||
this.startDate,
|
|
||||||
this.endDate,
|
|
||||||
this.receiveTimeSelected,
|
|
||||||
this.onReceiveTimeSelected,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DropdownButtonHideUnderline(
|
|
||||||
child: PointerInterceptor(
|
|
||||||
child: DropdownButton2<EmailReceiveTimeType>(
|
|
||||||
isExpanded: true,
|
|
||||||
items: EmailReceiveTimeType.values
|
|
||||||
.map((item) => _buildItemMenu(context, item))
|
|
||||||
.toList(),
|
|
||||||
value: receiveTimeSelected,
|
|
||||||
customButton: Container(
|
|
||||||
height: AdvancedSearchInputFormStyle.inputFieldHeight,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(
|
|
||||||
Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius),
|
|
||||||
),
|
|
||||||
border: Border.all(
|
|
||||||
color: AdvancedSearchInputFormStyle.inputFieldBorderColor,
|
|
||||||
width: AdvancedSearchInputFormStyle.inputFieldBorderWidth,
|
|
||||||
),
|
|
||||||
color: AdvancedSearchInputFormStyle.inputFieldBackgroundColor,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsetsDirectional.only(start: 12, end: 8),
|
|
||||||
child: Row(children: [
|
|
||||||
Expanded(child: Text(
|
|
||||||
receiveTimeSelected?.getTitle(context, startDate: startDate, endDate: endDate) ?? '',
|
|
||||||
style: AdvancedSearchInputFormStyle.inputTextStyle,
|
|
||||||
maxLines: 1,
|
|
||||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
|
||||||
overflow: CommonTextStyle.defaultTextOverFlow,
|
|
||||||
)),
|
|
||||||
SvgPicture.asset(imagePaths.icDropDown)
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
onReceiveTimeSelected?.call(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buttonStyleData: ButtonStyleData(
|
|
||||||
height: AdvancedSearchInputFormStyle.inputFieldHeight,
|
|
||||||
padding: const EdgeInsetsDirectional.only(start: 12, end: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(
|
|
||||||
Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius),
|
|
||||||
),
|
|
||||||
border: Border.all(
|
|
||||||
color: AdvancedSearchInputFormStyle.inputFieldBorderColor,
|
|
||||||
width: AdvancedSearchInputFormStyle.inputFieldBorderWidth,
|
|
||||||
),
|
|
||||||
color: AdvancedSearchInputFormStyle.inputFieldBackgroundColor,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
dropdownStyleData: DropdownStyleData(
|
|
||||||
maxHeight: 200,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
color: Colors.white),
|
|
||||||
elevation: 4,
|
|
||||||
offset: const Offset(0.0, -8.0),
|
|
||||||
scrollbarTheme: ScrollbarThemeData(
|
|
||||||
radius: const Radius.circular(40),
|
|
||||||
thickness: WidgetStateProperty.all<double>(6),
|
|
||||||
thumbVisibility: WidgetStateProperty.all<bool>(true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
iconStyleData: IconStyleData(
|
|
||||||
icon: SvgPicture.asset(imagePaths.icDropDown),
|
|
||||||
),
|
|
||||||
menuItemStyleData: const MenuItemStyleData(
|
|
||||||
height: 44,
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenuItem<EmailReceiveTimeType> _buildItemMenu(
|
|
||||||
BuildContext context,
|
|
||||||
EmailReceiveTimeType receiveTime
|
|
||||||
) {
|
|
||||||
return DropdownMenuItem<EmailReceiveTimeType>(
|
|
||||||
value: receiveTime,
|
|
||||||
child: PointerInterceptor(
|
|
||||||
child: Container(
|
|
||||||
color: Colors.transparent,
|
|
||||||
height: 44,
|
|
||||||
child: Row(children: [
|
|
||||||
Expanded(child: Text(
|
|
||||||
receiveTime.getTitle(context),
|
|
||||||
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Colors.black
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
|
||||||
overflow: CommonTextStyle.defaultTextOverFlow,
|
|
||||||
)),
|
|
||||||
if (receiveTime == receiveTimeSelected)
|
|
||||||
SvgPicture.asset(
|
|
||||||
imagePaths.icChecked,
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
fit: BoxFit.fill
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4619,5 +4619,27 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"enterSubjectKeywords": "Enter subject keywords...",
|
||||||
|
"@enterSubjectKeywords": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"addAnEmailAddress": "Add an email address",
|
||||||
|
"@addAnEmailAddress": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"dialogRecoverDeletedMessagesDescription": "You can recover messages deleted within the last {period} days. Recovered emails will be restored to your recovered folder.",
|
||||||
|
"@dialogRecoverDeletedMessagesDescription": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [
|
||||||
|
"period"
|
||||||
|
],
|
||||||
|
"placeholders": {
|
||||||
|
"period": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4869,4 +4869,26 @@ class AppLocalizations {
|
|||||||
name: 'createARule',
|
name: 'createARule',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get enterSubjectKeywords {
|
||||||
|
return Intl.message(
|
||||||
|
'Enter subject keywords...',
|
||||||
|
name: 'enterSubjectKeywords',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get addAnEmailAddress {
|
||||||
|
return Intl.message(
|
||||||
|
'Add an email address',
|
||||||
|
name: 'addAnEmailAddress',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dialogRecoverDeletedMessagesDescription(String period) {
|
||||||
|
return Intl.message(
|
||||||
|
'You can recover messages deleted within the last $period days. Recovered emails will be restored to your recovered folder.',
|
||||||
|
name: 'dialogRecoverDeletedMessagesDescription',
|
||||||
|
args: [period],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user