Skip to content

Commit 8173536

Browse files
dab246hoangdat
authored andcommitted
TF-2940 Add spell check for subject email
1 parent b4db2f8 commit 8173536

File tree

12 files changed

+164
-18
lines changed

12 files changed

+164
-18
lines changed

Diff for: contact/pubspec.lock

+17
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,15 @@ packages:
680680
url: "https://pub.dev"
681681
source: hosted
682682
version: "6.6.1"
683+
languagetool_textfield:
684+
dependency: transitive
685+
description:
686+
path: "."
687+
ref: twake-supported
688+
resolved-ref: f47dd9829e145acc795b4e3e62f8bc668135fcd6
689+
url: "https://github.com/dab246/languagetool_textfield.git"
690+
source: git
691+
version: "0.1.0"
683692
leak_tracker:
684693
dependency: transitive
685694
description:
@@ -1076,6 +1085,14 @@ packages:
10761085
url: "https://pub.dev"
10771086
source: hosted
10781087
version: "0.7.0"
1088+
throttling:
1089+
dependency: transitive
1090+
description:
1091+
name: throttling
1092+
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
1093+
url: "https://pub.dev"
1094+
source: hosted
1095+
version: "2.0.1"
10791096
timing:
10801097
dependency: transitive
10811098
description:

Diff for: core/lib/presentation/extensions/color_extension.dart

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extension AppColor on Color {
44
static const primaryColor = Color(0xFF007AFF);
55
static const primaryDarkColor = Color(0xFF1C1C1C);
66
static const primaryLightColor = Color(0xFFFFFFFF);
7+
static const primarySelectedColor = Color(0xFFDFEEFF);
78
static const baseTextColor = Color(0xFF7E869B);
89
static const textFieldTextColor = Color(0xFF7E869B);
910
static const textFieldLabelColor = Color(0xFF7E869B);
@@ -16,11 +17,8 @@ extension AppColor on Color {
1617
static const loginTextFieldFocusedBorder = Color(0xFF007AFF);
1718
static const loginTextFieldHintColor = Color(0xff818C99);
1819
static const loginTextFieldBackgroundColor = Color(0xFFF2F3F5);
19-
static const loginTextFieldBackgroundErrorColor = Color(0xFFFAEBEB);
20-
static const buttonColor = Color(0xFF837DFF);
2120
static const appColor = Color(0xFF3840F7);
2221
static const nameUserColor = Color(0xFF182952);
23-
static const emailUserColor = Color(0xFF7E869B);
2422
static const userInformationBackgroundColor = Color(0xFFF5F5F7);
2523
static const searchBorderColor = Color(0xFFEAEAEA);
2624
static const searchHintTextColor = Color(0xFF7E869B);
@@ -29,7 +27,6 @@ extension AppColor on Color {
2927
static const mailboxSelectedTextColor = Color(0xFF3840F7);
3028
static const mailboxTextColor = Color(0xFF182952);
3129
static const mailboxSelectedTextNumberColor = Color(0xFF182952);
32-
static const mailboxTextNumberColor = Color(0xFF837DFF);
3330
static const mailboxSelectedIconColor = Color(0xFF3840F7);
3431
static const mailboxIconColor = Color(0xFF7E869B);
3532
static const storageBackgroundColor = Color(0xFFF5F5F7);

Diff for: core/lib/presentation/utils/theme_utils.dart

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class ThemeUtils {
1111
fontFamily: ConstantsUI.fontApp,
1212
appBarTheme: _appBarTheme,
1313
textTheme: _textTheme,
14+
textSelectionTheme: _textSelectionTheme,
1415
dividerTheme: _dividerTheme,
1516
visualDensity: VisualDensity.adaptivePlatformDensity,
1617
scrollbarTheme: ScrollbarThemeData(
@@ -27,6 +28,14 @@ class ThemeUtils {
2728
);
2829
}
2930

31+
static TextSelectionThemeData get _textSelectionTheme {
32+
return const TextSelectionThemeData(
33+
cursorColor: AppColor.primaryColor,
34+
selectionHandleColor: AppColor.primaryColor,
35+
selectionColor: AppColor.primarySelectedColor,
36+
);
37+
}
38+
3039
static AppBarTheme get _appBarTheme {
3140
return const AppBarTheme(
3241
color: Colors.white,

Diff for: core/lib/presentation/views/text/text_field_builder.dart

+59-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:core/presentation/extensions/color_extension.dart';
22
import 'package:core/utils/direction_utils.dart';
33
import 'package:flutter/material.dart';
4+
import 'package:languagetool_textfield/languagetool_textfield.dart';
45

56
class TextFieldBuilder extends StatefulWidget {
67

@@ -10,7 +11,7 @@ class TextFieldBuilder extends StatefulWidget {
1011
final TapRegionCallback? onTapOutside;
1112
final TextStyle? textStyle;
1213
final TextInputAction? textInputAction;
13-
final InputDecoration? decoration;
14+
final InputDecoration decoration;
1415
final bool obscureText;
1516
final int? maxLines;
1617
final int? minLines;
@@ -25,6 +26,7 @@ class TextFieldBuilder extends StatefulWidget {
2526
final TextDirection textDirection;
2627
final bool readOnly;
2728
final MouseCursor? mouseCursor;
29+
final LanguageToolController? languageToolController;
2830

2931
const TextFieldBuilder({
3032
super.key,
@@ -36,10 +38,11 @@ class TextFieldBuilder extends StatefulWidget {
3638
this.textStyle = const TextStyle(color: AppColor.textFieldTextColor),
3739
this.textDirection = TextDirection.ltr,
3840
this.textInputAction,
39-
this.decoration,
41+
this.decoration = const InputDecoration(),
4042
this.maxLines,
4143
this.minLines,
4244
this.controller,
45+
this.languageToolController,
4346
this.keyboardType,
4447
this.focusNode,
4548
this.fromValue,
@@ -57,22 +60,67 @@ class TextFieldBuilder extends StatefulWidget {
5760

5861
class _TextFieldBuilderState extends State<TextFieldBuilder> {
5962

60-
late TextEditingController _controller;
63+
TextEditingController? _controller;
64+
LanguageToolController? _languageToolController;
65+
6166
late TextDirection _textDirection;
6267

6368
@override
6469
void initState() {
6570
super.initState();
66-
if (widget.fromValue != null) {
67-
_controller = TextEditingController.fromValue(TextEditingValue(text: widget.fromValue!));
71+
if (widget.languageToolController != null) {
72+
_languageToolController = widget.languageToolController;
73+
if (widget.fromValue != null) {
74+
_languageToolController?.value = TextEditingValue(text: widget.fromValue!);
75+
}
6876
} else {
69-
_controller = widget.controller ?? TextEditingController();
77+
if (widget.fromValue != null) {
78+
_controller = TextEditingController.fromValue(TextEditingValue(text: widget.fromValue!));
79+
} else {
80+
_controller = widget.controller ?? TextEditingController();
81+
}
7082
}
7183
_textDirection = widget.textDirection;
7284
}
7385

7486
@override
7587
Widget build(BuildContext context) {
88+
if (_languageToolController != null) {
89+
return LanguageToolTextField(
90+
key: widget.key,
91+
controller: _languageToolController!,
92+
cursorColor: widget.cursorColor,
93+
autocorrect: widget.autocorrect,
94+
textInputAction: widget.textInputAction,
95+
decoration: widget.decoration,
96+
maxLines: widget.maxLines,
97+
minLines: widget.minLines,
98+
keyboardAppearance: widget.keyboardAppearance,
99+
style: widget.textStyle,
100+
keyboardType: widget.keyboardType,
101+
autoFocus: widget.autoFocus,
102+
focusNode: widget.focusNode,
103+
alignCenter: false,
104+
textDirection: _textDirection,
105+
readOnly: widget.readOnly,
106+
mouseCursor: widget.mouseCursor,
107+
onTextChange: (value) {
108+
widget.onTextChange?.call(value);
109+
if (value.isNotEmpty) {
110+
final directionByText = DirectionUtils.getDirectionByEndsText(value);
111+
if (directionByText != _textDirection) {
112+
setState(() {
113+
_textDirection = directionByText;
114+
});
115+
}
116+
}
117+
},
118+
onTextSubmitted: widget.onTextSubmitted,
119+
onTap: widget.onTap,
120+
onTapOutside: widget.onTapOutside,
121+
);
122+
}
123+
76124
return TextField(
77125
key: widget.key,
78126
controller: _controller,
@@ -110,8 +158,11 @@ class _TextFieldBuilderState extends State<TextFieldBuilder> {
110158

111159
@override
112160
void dispose() {
113-
if (widget.fromValue == null && widget.controller == null) {
114-
_controller.dispose();
161+
if (widget.controller == null) {
162+
_controller?.dispose();
163+
}
164+
if (widget.languageToolController == null) {
165+
_languageToolController?.dispose();
115166
}
116167
super.dispose();
117168
}

Diff for: core/pubspec.lock

+17
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,15 @@ packages:
648648
url: "https://pub.dev"
649649
source: hosted
650650
version: "4.9.0"
651+
languagetool_textfield:
652+
dependency: "direct main"
653+
description:
654+
path: "."
655+
ref: twake-supported
656+
resolved-ref: f47dd9829e145acc795b4e3e62f8bc668135fcd6
657+
url: "https://github.com/dab246/languagetool_textfield.git"
658+
source: git
659+
version: "0.1.0"
651660
leak_tracker:
652661
dependency: transitive
653662
description:
@@ -1021,6 +1030,14 @@ packages:
10211030
url: "https://pub.dev"
10221031
source: hosted
10231032
version: "0.7.0"
1033+
throttling:
1034+
dependency: transitive
1035+
description:
1036+
name: throttling
1037+
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
1038+
url: "https://pub.dev"
1039+
source: hosted
1040+
version: "2.0.1"
10241041
timing:
10251042
dependency: transitive
10261043
description:

Diff for: core/pubspec.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ dependencies:
2626
flutter:
2727
sdk: flutter
2828

29+
### Dependencies from git ###
30+
# TODO: We will change it when the PR in upstream repository will be merged
31+
# https://github.com/solid-software/languagetool_textfield/pull/83
32+
languagetool_textfield:
33+
git:
34+
url: https://github.com/dab246/languagetool_textfield.git
35+
ref: twake-supported
36+
2937
### Dependencies from pub.dev ###
3038
cupertino_icons: 1.0.6
3139

Diff for: lib/features/composer/presentation/composer_controller.dart

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
2424
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
2525
import 'package:jmap_dart_client/jmap/mail/email/individual_header_identifier.dart';
2626
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
27+
import 'package:languagetool_textfield/languagetool_textfield.dart';
2728
import 'package:model/model.dart';
2829
import 'package:permission_handler/permission_handler.dart';
2930
import 'package:pointer_interceptor/pointer_interceptor.dart';
@@ -150,7 +151,9 @@ class ComposerController extends BaseController with DragDropFileMixin implement
150151
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
151152
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
152153

153-
final subjectEmailInputController = TextEditingController();
154+
final subjectEmailInputController = LanguageToolController(
155+
delay: const Duration(milliseconds: 200),
156+
);
154157
final toEmailAddressController = TextEditingController();
155158
final ccEmailAddressController = TextEditingController();
156159
final bccEmailAddressController = TextEditingController();
@@ -1942,6 +1945,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
19421945

19431946
void handleOnFocusHtmlEditorWeb() {
19441947
FocusManager.instance.primaryFocus?.unfocus();
1948+
subjectEmailInputController.popupWidget?.popupRenderer.dismiss();
19451949
richTextWebController?.editorController.setFocus();
19461950
richTextWebController?.closeAllMenuPopup();
19471951
}

Diff for: lib/features/composer/presentation/styles/composer_style.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ class ComposerStyle {
1919
static const EdgeInsetsGeometry desktopRecipientPadding = EdgeInsetsDirectional.only(end: 24);
2020
static const EdgeInsetsGeometry desktopRecipientMargin = EdgeInsetsDirectional.only(start: 24);
2121
static const EdgeInsetsGeometry desktopSubjectMargin = EdgeInsetsDirectional.only(start: 24);
22-
static const EdgeInsetsGeometry desktopSubjectPadding = EdgeInsetsDirectional.only(end: 24, top: 12, bottom: 12);
22+
static const EdgeInsetsGeometry desktopSubjectPadding = EdgeInsetsDirectional.only(end: 24);
2323
static const EdgeInsetsGeometry desktopEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 20);
2424
static const EdgeInsetsGeometry tabletRecipientPadding = EdgeInsetsDirectional.only(end: 24);
2525
static const EdgeInsetsGeometry tabletRecipientMargin = EdgeInsetsDirectional.only(start: 24);
2626
static const EdgeInsetsGeometry tabletSubjectMargin = EdgeInsetsDirectional.only(start: 24);
27-
static const EdgeInsetsGeometry tabletSubjectPadding = EdgeInsetsDirectional.only(end: 24, top: 12, bottom: 12);
27+
static const EdgeInsetsGeometry tabletSubjectPadding = EdgeInsetsDirectional.only(end: 24);
2828
static const EdgeInsetsGeometry tabletEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 20);
2929
static const EdgeInsetsGeometry mobileRecipientPadding = EdgeInsetsDirectional.only(end: 16);
3030
static const EdgeInsetsGeometry mobileRecipientMargin = EdgeInsetsDirectional.only(start: 16);
3131
static const EdgeInsetsGeometry mobileSubjectMargin = EdgeInsetsDirectional.only(start: 16);
32-
static const EdgeInsetsGeometry mobileSubjectPadding = EdgeInsetsDirectional.only(end: 16, top: 12, bottom: 12);
32+
static const EdgeInsetsGeometry mobileSubjectPadding = EdgeInsetsDirectional.only(end: 16);
3333
static const EdgeInsetsGeometry mobileEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 12);
3434
static const EdgeInsetsGeometry popupItemPadding = EdgeInsetsDirectional.symmetric(horizontal: 12);
3535
static const EdgeInsetsGeometry insertImageLoadingBarPadding = EdgeInsetsDirectional.only(top: 12);

Diff for: lib/features/composer/presentation/widgets/subject_composer_widget.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import 'package:core/presentation/views/text/text_field_builder.dart';
22
import 'package:core/utils/direction_utils.dart';
33
import 'package:flutter/material.dart';
4+
import 'package:languagetool_textfield/languagetool_textfield.dart';
45
import 'package:tmail_ui_user/features/composer/presentation/styles/subject_composer_widget_style.dart';
56
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
67

78
class SubjectComposerWidget extends StatelessWidget {
89

910
final FocusNode? focusNode;
10-
final TextEditingController textController;
11+
final LanguageToolController textController;
1112
final ValueChanged<String>? onTextChange;
1213
final EdgeInsetsGeometry? margin;
1314
final EdgeInsetsGeometry? padding;
@@ -47,9 +48,10 @@ class SubjectComposerWidget extends StatelessWidget {
4748
focusNode: focusNode,
4849
onTextChange: onTextChange,
4950
maxLines: 1,
51+
decoration: const InputDecoration(border: InputBorder.none),
5052
textDirection: DirectionUtils.getDirectionByLanguage(context),
5153
textStyle: SubjectComposerWidgetStyle.inputTextStyle,
52-
controller: textController,
54+
languageToolController: textController,
5355
)
5456
)
5557
]

Diff for: model/pubspec.lock

+17
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,15 @@ packages:
672672
url: "https://pub.dev"
673673
source: hosted
674674
version: "6.6.1"
675+
languagetool_textfield:
676+
dependency: transitive
677+
description:
678+
path: "."
679+
ref: twake-supported
680+
resolved-ref: f47dd9829e145acc795b4e3e62f8bc668135fcd6
681+
url: "https://github.com/dab246/languagetool_textfield.git"
682+
source: git
683+
version: "0.1.0"
675684
leak_tracker:
676685
dependency: transitive
677686
description:
@@ -1053,6 +1062,14 @@ packages:
10531062
url: "https://pub.dev"
10541063
source: hosted
10551064
version: "0.7.0"
1065+
throttling:
1066+
dependency: transitive
1067+
description:
1068+
name: throttling
1069+
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
1070+
url: "https://pub.dev"
1071+
source: hosted
1072+
version: "2.0.1"
10561073
timing:
10571074
dependency: transitive
10581075
description:

Diff for: pubspec.lock

+17
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,15 @@ packages:
12641264
url: "https://pub.dev"
12651265
source: hosted
12661266
version: "6.6.1"
1267+
languagetool_textfield:
1268+
dependency: "direct main"
1269+
description:
1270+
path: "."
1271+
ref: twake-supported
1272+
resolved-ref: f47dd9829e145acc795b4e3e62f8bc668135fcd6
1273+
url: "https://github.com/dab246/languagetool_textfield.git"
1274+
source: git
1275+
version: "0.1.0"
12671276
leak_tracker:
12681277
dependency: transitive
12691278
description:
@@ -1917,6 +1926,14 @@ packages:
19171926
url: "https://pub.dev"
19181927
source: hosted
19191928
version: "2.0.2"
1929+
throttling:
1930+
dependency: transitive
1931+
description:
1932+
name: throttling
1933+
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
1934+
url: "https://pub.dev"
1935+
source: hosted
1936+
version: "2.0.1"
19201937
timeago:
19211938
dependency: "direct main"
19221939
description:

0 commit comments

Comments
 (0)