TF-138 Add domain layer for search mail with text pattern

This commit is contained in:
dab246
2021-10-26 13:17:12 +07:00
committed by Dat H. Pham
parent 03f5921025
commit 62d416c4a4
11 changed files with 254 additions and 4 deletions
+1
View File
@@ -25,6 +25,7 @@ export 'presentation/views/text/slogan_builder.dart';
export 'presentation/views/text/text_field_builder.dart';
export 'presentation/views/text/input_decoration_builder.dart';
export 'presentation/views/text/text_builder.dart';
export 'presentation/views/text/rich_text_builder.dart';
export 'presentation/views/responsive/responsive_widget.dart';
export 'presentation/views/list/tree_view.dart';
export 'presentation/views/button/button_builder.dart';
@@ -59,4 +59,7 @@ extension AppColor on Color {
static const toastWithActionBackgroundColor = Color(0xFF3F3F3F);
static const buttonActionToastWithActionColor = Color(0xFF7ADCF8);
static const backgroundCountAttachment = Color(0x681C1C1C);
static const bgStatusResultSearch = Color(0xFFF5F5F7);
static const bgWordSearch = Color(0xFFD7D6FC);
}
@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
class RichTextBuilder {
final String _textOrigin;
final String _wordToStyle;
final TextStyle _styleOrigin;
final TextStyle _styleWord;
Key? _key;
int? _maxLines;
TextOverflow? _overflow;
RichTextBuilder(
this._textOrigin,
this._wordToStyle,
this._styleOrigin,
this._styleWord,
);
void key(Key key) {
_key = key;
}
void maxLines(int maxLines) {
_maxLines = maxLines;
}
void setOverflow(TextOverflow textOverflow) {
_overflow = textOverflow;
}
RichText build() {
return RichText(
key: _key,
maxLines: _maxLines ?? 1,
overflow: _overflow ?? TextOverflow.ellipsis,
text: TextSpan(
style: _styleOrigin,
children: _getSpans(_textOrigin, _wordToStyle, _styleWord)));
}
List<TextSpan> _getSpans(String text, String matchWord, TextStyle style) {
List<TextSpan> spans = [];
int spanBoundary = 0;
do {
// look for the next match
final startIndex = text.toLowerCase().indexOf(matchWord.toLowerCase(), spanBoundary);
// if no more matches then add the rest of the string without style
if (startIndex == -1) {
spans.add(TextSpan(text: text.substring(spanBoundary)));
return spans;
}
// add any unStyled text before the next match
if (startIndex > spanBoundary) {
spans.add(TextSpan(text: text.substring(spanBoundary, startIndex)));
}
// style the matched text
final endIndex = startIndex + matchWord.length;
final spanText = text.substring(startIndex, endIndex);
spans.add(TextSpan(text: spanText, style: style));
// mark the boundary to start the next search from
spanBoundary = endIndex;
// continue until there are no more matches
} while (spanBoundary < text.length);
return spans;
}
}
@@ -1,9 +1,12 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
typedef OnOpenSearchMailActionClick = void Function();
class TextFieldBuilder {
Key? _key;
ValueChanged<String>? _onTextChange;
ValueChanged<String>? _onTextSubmitted;
TextStyle? _textStyle;
TextInputAction? _textInputAction;
InputDecoration? _inputDecoration;
@@ -11,6 +14,9 @@ class TextFieldBuilder {
int? _maxLines = 1;
TextEditingController? _textController;
TextInputType? _keyboardType;
Color? _cursorColor;
bool? _autoFocus;
FocusNode? _focusNode;
void key(Key key) {
_key = key;
@@ -20,6 +26,10 @@ class TextFieldBuilder {
_onTextChange = onChange;
}
void onSubmitted(ValueChanged<String> onSubmitted) {
_onTextSubmitted = onSubmitted;
}
void textStyle(TextStyle style) {
_textStyle = style;
}
@@ -40,7 +50,7 @@ class TextFieldBuilder {
_textController = TextEditingController.fromValue(TextEditingValue(text: value));
}
void addController(TextEditingController textEditingController) {
void addController(TextEditingController? textEditingController) {
_textController = textEditingController;
}
@@ -52,11 +62,23 @@ class TextFieldBuilder {
_keyboardType = value;
}
void cursorColor(Color? color) {
_cursorColor = color;
}
void autoFocus(bool autoFocus) {
_autoFocus = autoFocus;
}
void addFocusNode(FocusNode? focusNode) {
_focusNode = focusNode;
}
TextField build() {
return TextField(
key: _key ?? Key('TextFieldBuilder'),
onChanged: _onTextChange,
cursorColor: AppColor.primaryColor,
cursorColor: _cursorColor ?? AppColor.primaryColor,
controller: _textController,
autocorrect: false,
textInputAction: _textInputAction,
@@ -66,6 +88,9 @@ class TextFieldBuilder {
style: _textStyle ?? TextStyle(color: AppColor.textFieldTextColor),
obscureText: _obscureText ?? false,
keyboardType: _keyboardType,
onSubmitted: _onTextSubmitted,
autofocus: _autoFocus ?? false,
focusNode: _focusNode,
);
}
}
@@ -0,0 +1,11 @@
import 'package:equatable/equatable.dart';
class SearchQuery with EquatableMixin {
final String value;
SearchQuery(this.value);
@override
List<Object?> get props => [value];
}
@@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
@@ -40,4 +41,14 @@ abstract class ThreadRepository {
Properties? properties,
}
);
Future<List<Email>> searchEmails(
AccountId accountId,
{
UnsignedInt? limit,
Set<Comparator>? sort,
Filter? filter,
Properties? properties,
}
);
}
@@ -0,0 +1,39 @@
import 'package:core/core.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
class SearchingState extends UIState {
SearchingState();
@override
List<Object> get props => [];
}
class SearchEmailNewQuery extends UIState {
final SearchQuery searchQuery;
SearchEmailNewQuery(this.searchQuery);
@override
List<Object> get props => [searchQuery];
}
class SearchEmailSuccess extends UIState {
final List<PresentationEmail> emailList;
SearchEmailSuccess(this.emailList);
@override
List<Object> get props => [emailList];
}
class SearchEmailFailure extends FeatureFailure {
final exception;
SearchEmailFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,47 @@
import 'package:core/core.dart';
import 'package:model/model.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
class SearchEmailInteractor {
final ThreadRepository threadRepository;
SearchEmailInteractor(this.threadRepository);
Stream<Either<Failure, Success>> execute(
AccountId accountId,
{
UnsignedInt? limit,
Set<Comparator>? sort,
Filter? filter,
Properties? properties,
}
) async* {
try {
yield Right(SearchingState());
final emailList = await threadRepository.searchEmails(
accountId,
limit: limit,
sort: sort,
filter: filter,
properties: properties);
final presentationEmailList = emailList
.map((email) => email.toPresentationEmail())
.toList();
yield Right(SearchEmailSuccess(presentationEmailList));
} catch (e) {
yield Left(SearchEmailFailure(e));
}
}
}
+8 -2
View File
@@ -5,8 +5,8 @@ import 'package:jmap_dart_client/jmap/core/utc_date.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
import 'package:model/mailbox/select_mode.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
class PresentationEmail with EquatableMixin {
@@ -23,6 +23,8 @@ class PresentationEmail with EquatableMixin {
final Set<EmailAddress>? cc;
final Set<EmailAddress>? bcc;
final Set<EmailAddress>? replyTo;
final Map<MailboxId, bool>? mailboxIds;
final List<MailboxName?>? mailboxNames;
final SelectMode selectMode;
PresentationEmail(
@@ -40,6 +42,8 @@ class PresentationEmail with EquatableMixin {
this.cc,
this.bcc,
this.replyTo,
this.mailboxIds,
this.mailboxNames,
this.selectMode = SelectMode.INACTIVE
}
);
@@ -65,6 +69,8 @@ class PresentationEmail with EquatableMixin {
bool isFlaggedEmail() => keywords?.containsKey(KeyWordIdentifier.emailFlagged) == true;
String get mailboxName => mailboxNames?.first?.name ?? '';
@override
List<Object?> get props => [
id,
@@ -57,6 +57,7 @@ extension EmailExtension on Email {
cc: cc,
bcc: bcc,
replyTo: replyTo,
mailboxIds: mailboxIds,
selectMode: selectMode
);
}
@@ -34,6 +34,8 @@ extension PresentationEmailExtension on PresentationEmail {
cc: cc,
bcc: bcc,
replyTo: replyTo,
mailboxIds: mailboxIds,
mailboxNames: mailboxNames,
selectMode: selectMode == SelectMode.INACTIVE ? SelectMode.ACTIVE : SelectMode.INACTIVE
);
}
@@ -53,6 +55,8 @@ extension PresentationEmailExtension on PresentationEmail {
cc: cc,
bcc: bcc,
replyTo: replyTo,
mailboxIds: mailboxIds,
mailboxNames: mailboxNames,
selectMode: selectMode
);
}
@@ -102,4 +106,37 @@ extension PresentationEmailExtension on PresentationEmail {
return Tuple3([], [], []);
}
}
PresentationEmail toSearchPresentationEmail(Map<MailboxId, PresentationMailbox> mapMailboxes) {
mailboxIds?.removeWhere((key, value) => !value);
final listMailboxId = mailboxIds?.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
final listMailboxName = listMailboxId
?.map((mailboxId) => mapMailboxes.containsKey(mailboxId) ? mapMailboxes[mailboxId]?.name : null)
.where((mailboxName) => mailboxName != null)
.toList();
return PresentationEmail(
this.id,
keywords: keywords,
size: size,
receivedAt: receivedAt,
hasAttachment: hasAttachment,
preview: preview,
subject: subject,
sentAt: sentAt,
from: from,
to: to,
cc: cc,
bcc: bcc,
replyTo: replyTo,
mailboxIds: mailboxIds,
mailboxNames: listMailboxName,
selectMode: selectMode
);
}
}