From 17bad5ec51c690a808ade4a9c5639957f0b79380 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 26 Aug 2021 17:03:04 +0700 Subject: [PATCH] TF-46 Create object, builder for model and core module --- assets/images/ic_composer_close.svg | 4 ++ assets/images/ic_composer_file_share.svg | 4 ++ assets/images/ic_composer_menu.svg | 5 ++ assets/images/ic_composer_send.svg | 11 ++++ assets/images/ic_expand_address.svg | 3 ++ core/lib/core.dart | 1 + .../domain/extensions/datetime_extension.dart | 18 +++++++ .../extensions/color_extension.dart | 6 +++ .../extensions/html_extension.dart | 14 +++++ .../presentation/resources/image_paths.dart | 5 ++ .../validator/html_message_purifier.dart | 3 +- .../views/button/button_builder.dart | 10 +++- .../views/text/text_field_builder.dart | 14 +++++ .../views/text/text_form_field_builder.dart | 53 +++++++++++++++++++ model/lib/email/email_action_type.dart | 7 +++ model/lib/email/prefix_email_address.dart | 6 +++ model/lib/email/presentation_email.dart | 2 +- .../extensions/email_address_extension.dart | 20 ++++++- .../list_email_address_extension.dart | 11 ++-- model/lib/extensions/utcdate_extension.dart | 13 +++++ model/lib/model.dart | 5 +- 21 files changed, 203 insertions(+), 12 deletions(-) create mode 100644 assets/images/ic_composer_close.svg create mode 100644 assets/images/ic_composer_file_share.svg create mode 100644 assets/images/ic_composer_menu.svg create mode 100644 assets/images/ic_composer_send.svg create mode 100644 assets/images/ic_expand_address.svg create mode 100644 core/lib/presentation/views/text/text_form_field_builder.dart create mode 100644 model/lib/email/email_action_type.dart create mode 100644 model/lib/email/prefix_email_address.dart create mode 100644 model/lib/extensions/utcdate_extension.dart diff --git a/assets/images/ic_composer_close.svg b/assets/images/ic_composer_close.svg new file mode 100644 index 000000000..cf7617e16 --- /dev/null +++ b/assets/images/ic_composer_close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/ic_composer_file_share.svg b/assets/images/ic_composer_file_share.svg new file mode 100644 index 000000000..81dbfd4f0 --- /dev/null +++ b/assets/images/ic_composer_file_share.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/ic_composer_menu.svg b/assets/images/ic_composer_menu.svg new file mode 100644 index 000000000..7bffa5884 --- /dev/null +++ b/assets/images/ic_composer_menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/ic_composer_send.svg b/assets/images/ic_composer_send.svg new file mode 100644 index 000000000..33387e9bb --- /dev/null +++ b/assets/images/ic_composer_send.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/ic_expand_address.svg b/assets/images/ic_expand_address.svg new file mode 100644 index 000000000..93c15c3dd --- /dev/null +++ b/assets/images/ic_expand_address.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/lib/core.dart b/core/lib/core.dart index 59ef23830..4a170eb8e 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -25,6 +25,7 @@ export 'presentation/views/list/tree_view.dart'; export 'presentation/views/button/button_builder.dart'; export 'presentation/views/image/avatar_builder.dart'; export 'presentation/views/list/sliver_grid_delegate_fixed_height.dart'; +export 'presentation/views/text/text_form_field_builder.dart'; // Resources export 'presentation/resources/assets_paths.dart'; diff --git a/core/lib/domain/extensions/datetime_extension.dart b/core/lib/domain/extensions/datetime_extension.dart index 1b95a08e7..f7d602fcd 100644 --- a/core/lib/domain/extensions/datetime_extension.dart +++ b/core/lib/domain/extensions/datetime_extension.dart @@ -16,3 +16,21 @@ extension DateTimeExtension on DateTime { return now.year == this.year; } } + +extension DateTimeNullableExtension on DateTime? { + + String toPattern() { + if (this != null) { + if (this!.isToday()) { + return 'h:mm a'; + } else if (this!.isYesterday()) { + return 'EEE'; + } else if (this!.isThisYear()) { + return 'MMMd'; + } else { + return 'yMMMd'; + } + } + return 'yMMMd'; + } +} diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 23f4f4556..8d5686439 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -46,4 +46,10 @@ extension AppColor on Color { static const avatarTextColor = Color(0xFF3840F7); static const sentTimeTextColorUnRead = Color(0xFF182952); static const subjectEmailTextColorUnRead = Color(0xFF3840F7); + static const dividerColor = Color(0xFFEAEAEA); + static const bgComposer = Color(0xFFFBFBFF); + static const emailAddressChipColor = Color(0xFFF5F5F7); + static const enableSendEmailButtonColor = Color(0xFF3840F7); + static const disableSendEmailButtonColor = Color(0xFFACAFFF); + static const borderLeftEmailContentColor = Color(0xFFEFEFEF); } diff --git a/core/lib/presentation/extensions/html_extension.dart b/core/lib/presentation/extensions/html_extension.dart index 83db272e5..03199d13a 100644 --- a/core/lib/presentation/extensions/html_extension.dart +++ b/core/lib/presentation/extensions/html_extension.dart @@ -12,4 +12,18 @@ extension HtmlExtension on String { String removeMaxHeightZeroPixel() => replaceAll(RegExp('max-height:0px;'), ''); String changeStyleBackground() => replaceAll(RegExp('background:'), 'background-color:'); + + String addBorderLefForBlockQuote() => replaceAll(RegExp(' attribute != null ? '<$tag $attribute>$this' : '<$tag>$this'; + + String addNewLineTag({int count = 1}) { + if (count == 1) return '$this
'; + + var htmlString = this; + for (var i = 0; i < count; i++) { + htmlString = '$htmlString
'; + } + return htmlString; + } } diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index fdd3369fe..427ea123b 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -31,6 +31,11 @@ class ImagePaths { String get icOffline => _getImagePath('ic_offline.svg'); String get icFlagged => _getImagePath('ic_flagged.svg'); String get icMoreReceiver => _getImagePath('ic_more_receiver.svg'); + String get icComposerClose => _getImagePath('ic_composer_close.svg'); + String get icComposerSend => _getImagePath('ic_composer_send.svg'); + String get icComposerMenu => _getImagePath('ic_composer_menu.svg'); + String get icComposerFileShare => _getImagePath('ic_composer_file_share.svg'); + String get icExpandAddress => _getImagePath('ic_expand_address.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/core/lib/presentation/validator/html_message_purifier.dart b/core/lib/presentation/validator/html_message_purifier.dart index 6516e27e6..e06bd9a6a 100644 --- a/core/lib/presentation/validator/html_message_purifier.dart +++ b/core/lib/presentation/validator/html_message_purifier.dart @@ -20,7 +20,8 @@ class HtmlMessagePurifier { .removeMaxHeightZeroPixel() .removeMaxWidthZeroPixel() .removeHeightZeroPixel() - .removeWidthZeroPixel(); + .removeWidthZeroPixel() + .addBorderLefForBlockQuote(); } String sanitizeHtml( diff --git a/core/lib/presentation/views/button/button_builder.dart b/core/lib/presentation/views/button/button_builder.dart index 704543896..74747961f 100644 --- a/core/lib/presentation/views/button/button_builder.dart +++ b/core/lib/presentation/views/button/button_builder.dart @@ -16,6 +16,7 @@ class ButtonBuilder { double? _padding; bool? _isVertical; Key? _key; + Color? _color; ButtonBuilder key(Key key) { _key = key; @@ -27,6 +28,11 @@ class ButtonBuilder { return this; } + ButtonBuilder color(Color color) { + _color = color; + return this; + } + ButtonBuilder padding(double padding) { _padding = padding; return this; @@ -40,7 +46,7 @@ class ButtonBuilder { ButtonBuilder(this._icon); - ButtonBuilder onBackActionClick(OnPressActionClick onPressActionClick) { + ButtonBuilder onPressActionClick(OnPressActionClick onPressActionClick) { _onPressActionClick = onPressActionClick; return this; } @@ -88,7 +94,7 @@ class ButtonBuilder { Widget _buildIcon() => Padding( padding: EdgeInsets.all(_padding ?? 10), - child: SvgPicture.asset(_icon ?? '', width: _size ?? 24, height: _size ?? 24, fit: BoxFit.fill)); + child: SvgPicture.asset(_icon ?? '', width: _size ?? 24, height: _size ?? 24, fit: BoxFit.fill, color: _color)); Widget _buildText() { return Text( diff --git a/core/lib/presentation/views/text/text_field_builder.dart b/core/lib/presentation/views/text/text_field_builder.dart index 46f919ddb..adbcbd026 100644 --- a/core/lib/presentation/views/text/text_field_builder.dart +++ b/core/lib/presentation/views/text/text_field_builder.dart @@ -8,6 +8,8 @@ class TextFieldBuilder { TextInputAction? _textInputAction; InputDecoration? _inputDecoration; bool? _obscureText; + int? _maxLines = 1; + TextEditingController? _textController; TextFieldBuilder key(Key key) { _key = key; @@ -39,13 +41,25 @@ class TextFieldBuilder { return this; } + TextFieldBuilder setText(String value) { + _textController = TextEditingController.fromValue(TextEditingValue(text: value)); + return this; + } + + TextFieldBuilder maxLines(int? value) { + _maxLines = value; + return this; + } + TextField build() { return TextField( key: _key ?? Key('TextFieldBuilder'), onChanged: _onTextChange, cursorColor: AppColor.primaryColor, + controller: _textController, textInputAction: _textInputAction, decoration: _inputDecoration, + maxLines: _maxLines, style: _textStyle ?? TextStyle(color: AppColor.textFieldTextColor), obscureText: _obscureText ?? false, ); diff --git a/core/lib/presentation/views/text/text_form_field_builder.dart b/core/lib/presentation/views/text/text_form_field_builder.dart new file mode 100644 index 000000000..00245f831 --- /dev/null +++ b/core/lib/presentation/views/text/text_form_field_builder.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +typedef OnTapActionClick = void Function(); + +class TextFormFieldBuilder { + Key? _key; + ValueChanged? _onTextChange; + InputDecoration? _inputDecoration; + TextInputAction? _textInputAction; + TextStyle? _textStyle; + OnTapActionClick? _onTapActionClick; + + void key(Key key) { + _key = key; + } + + void onChange(ValueChanged onChange) { + _onTextChange = onChange; + } + + void textStyle(TextStyle style) { + _textStyle = style; + } + + void textDecoration(InputDecoration inputDecoration) { + _inputDecoration = inputDecoration; + } + + void textInputAction(TextInputAction inputAction) { + _textInputAction = inputAction; + } + + void addOnTapActionClick(OnTapActionClick onTapActionClick) { + _onTapActionClick = onTapActionClick; + } + + TextFormField build() { + return TextFormField( + key: _key ?? Key('text_form_field_builder'), + keyboardType: TextInputType.multiline, + maxLines: null, + onChanged: _onTextChange, + style: _textStyle, + decoration: _inputDecoration, + textInputAction: _textInputAction, + onTap: () { + if (_onTapActionClick != null) { + _onTapActionClick!(); + } + }, + ); + } +} \ No newline at end of file diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart new file mode 100644 index 000000000..5009b6ebc --- /dev/null +++ b/model/lib/email/email_action_type.dart @@ -0,0 +1,7 @@ + +enum EmailActionType { + reply, + forward, + replyAll, + compose, +} \ No newline at end of file diff --git a/model/lib/email/prefix_email_address.dart b/model/lib/email/prefix_email_address.dart new file mode 100644 index 000000000..a4ddba94c --- /dev/null +++ b/model/lib/email/prefix_email_address.dart @@ -0,0 +1,6 @@ + +enum PrefixEmailAddress { + to, + cc, + bcc +} \ No newline at end of file diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart index 90d27f29f..f114e5de8 100644 --- a/model/lib/email/presentation_email.dart +++ b/model/lib/email/presentation_email.dart @@ -53,7 +53,7 @@ class PresentationEmail with EquatableMixin { String getAvatarText() { if (getSenderName().isNotEmpty) { - return getSenderName().substring(0, 1).toUpperCase(); + return getSenderName()[0].toUpperCase(); } return ''; } diff --git a/model/lib/extensions/email_address_extension.dart b/model/lib/extensions/email_address_extension.dart index 3241d3218..63e0cfeae 100644 --- a/model/lib/extensions/email_address_extension.dart +++ b/model/lib/extensions/email_address_extension.dart @@ -3,11 +3,27 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; extension EmailAddressExtension on EmailAddress { String asString() { - if (name != null && name!.isNotEmpty) { + if (getName().isNotEmpty) { return name!; - } else if (email != null && email!.isNotEmpty) { + } else if (getEmail().isNotEmpty) { return email!; } return ''; } + + String asFullString() { + if (getName().isNotEmpty) { + if (getEmail().isNotEmpty) { + return '${name!} <${email!}>'; + } + return name!; + } else if (getEmail().isNotEmpty) { + return email!; + } + return ''; + } + + String getEmail() => email != null ? email! : ''; + + String getName() => name != null ? name! : ''; } \ No newline at end of file diff --git a/model/lib/extensions/list_email_address_extension.dart b/model/lib/extensions/list_email_address_extension.dart index c618186f5..646d165a6 100644 --- a/model/lib/extensions/list_email_address_extension.dart +++ b/model/lib/extensions/list_email_address_extension.dart @@ -4,20 +4,21 @@ import 'package:model/extensions/email_address_extension.dart'; extension ListEmailAddressExtension on Set? { - List getListEmailAddress({ExpandMode expandMode = ExpandMode.EXPAND, int limitAddress = 1}) { + List getListEmailAddress({ExpandMode expandMode = ExpandMode.EXPAND, int limitAddress = 1, bool isFullEmailAddress = false}) { if (this != null) { if (expandMode == ExpandMode.EXPAND) { - return this!.map((emailAddress) => emailAddress.asString()).toList(); + return this!.map((emailAddress) => isFullEmailAddress ? emailAddress.asFullString() : emailAddress.asString()).toList(); } else { - final address = this!.map((emailAddress) => emailAddress.asString()).toList(); + final address = this!.map((emailAddress) => isFullEmailAddress ? emailAddress.asFullString() : emailAddress.asString()).toList(); return address.length > limitAddress ? address.sublist(0, limitAddress) : address; } } return []; } - String listEmailAddressToString({ExpandMode expandMode = ExpandMode.EXPAND, int limitAddress = 1}) { - return getListEmailAddress(expandMode: expandMode, limitAddress: limitAddress).join(', '); + String listEmailAddressToString({ExpandMode expandMode = ExpandMode.EXPAND, int limitAddress = 1, bool isFullEmailAddress = false}) { + final listEmail = getListEmailAddress(expandMode: expandMode, limitAddress: limitAddress, isFullEmailAddress: isFullEmailAddress); + return listEmail.isNotEmpty ? listEmail.join(', ') : ''; } int numberEmailAddress() => this != null ? this!.length : 0; diff --git a/model/lib/extensions/utcdate_extension.dart b/model/lib/extensions/utcdate_extension.dart new file mode 100644 index 000000000..95b9a4d56 --- /dev/null +++ b/model/lib/extensions/utcdate_extension.dart @@ -0,0 +1,13 @@ +import 'package:intl/intl.dart'; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; + +extension UTCDateExtension on UTCDate? { + + String formatDate({String pattern = 'yMMMd', String locale = 'en_US'}) { + if (this != null) { + return DateFormat(pattern, locale).format(this!.value); + } else { + return ''; + } + } +} \ No newline at end of file diff --git a/model/lib/model.dart b/model/lib/model.dart index 4543ef555..dbdc63603 100644 --- a/model/lib/model.dart +++ b/model/lib/model.dart @@ -19,9 +19,12 @@ export 'mailbox/expand_mode.dart'; // Email export 'email/presentation_email.dart'; export 'email/email_content.dart'; +export 'email/prefix_email_address.dart'; +export 'email/email_action_type.dart'; export 'email/presentation_email_address.dart'; // Extensions export 'extensions/email_address_extension.dart'; export 'extensions/list_email_address_extension.dart'; -export 'extensions/session_extension.dart'; \ No newline at end of file +export 'extensions/session_extension.dart'; +export 'extensions/utcdate_extension.dart'; \ No newline at end of file