Skip to content

Commit

Permalink
TF-2940 Add spell check for subject email
Browse files Browse the repository at this point in the history
  • Loading branch information
dab246 committed Aug 5, 2024
1 parent d6f78e4 commit 4f1f8ec
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 18 deletions.
17 changes: 17 additions & 0 deletions contact/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.6.1"
languagetool_textfield:
dependency: transitive
description:
path: "."
ref: twake-supported
resolved-ref: ae95b4c49af0dbfb7161cf1256027048751160e5
url: "https://github.com/dab246/languagetool_textfield.git"
source: git
version: "0.1.0"
leak_tracker:
dependency: transitive
description:
Expand Down Expand Up @@ -1036,6 +1045,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
throttling:
dependency: transitive
description:
name: throttling
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
url: "https://pub.dev"
source: hosted
version: "2.0.1"
timing:
dependency: transitive
description:
Expand Down
5 changes: 1 addition & 4 deletions core/lib/presentation/extensions/color_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extension AppColor on Color {
static const primaryColor = Color(0xFF007AFF);
static const primaryDarkColor = Color(0xFF1C1C1C);
static const primaryLightColor = Color(0xFFFFFFFF);
static const primarySelectedColor = Color(0xFFDFEEFF);
static const baseTextColor = Color(0xFF7E869B);
static const textFieldTextColor = Color(0xFF7E869B);
static const textFieldLabelColor = Color(0xFF7E869B);
Expand All @@ -16,11 +17,8 @@ extension AppColor on Color {
static const loginTextFieldFocusedBorder = Color(0xFF007AFF);
static const loginTextFieldHintColor = Color(0xff818C99);
static const loginTextFieldBackgroundColor = Color(0xFFF2F3F5);
static const loginTextFieldBackgroundErrorColor = Color(0xFFFAEBEB);
static const buttonColor = Color(0xFF837DFF);
static const appColor = Color(0xFF3840F7);
static const nameUserColor = Color(0xFF182952);
static const emailUserColor = Color(0xFF7E869B);
static const userInformationBackgroundColor = Color(0xFFF5F5F7);
static const searchBorderColor = Color(0xFFEAEAEA);
static const searchHintTextColor = Color(0xFF7E869B);
Expand All @@ -29,7 +27,6 @@ extension AppColor on Color {
static const mailboxSelectedTextColor = Color(0xFF3840F7);
static const mailboxTextColor = Color(0xFF182952);
static const mailboxSelectedTextNumberColor = Color(0xFF182952);
static const mailboxTextNumberColor = Color(0xFF837DFF);
static const mailboxSelectedIconColor = Color(0xFF3840F7);
static const mailboxIconColor = Color(0xFF7E869B);
static const storageBackgroundColor = Color(0xFFF5F5F7);
Expand Down
9 changes: 9 additions & 0 deletions core/lib/presentation/utils/theme_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ThemeUtils {
fontFamily: ConstantsUI.fontApp,
appBarTheme: _appBarTheme,
textTheme: _textTheme,
textSelectionTheme: _textSelectionTheme,
dividerTheme: _dividerTheme,
visualDensity: VisualDensity.adaptivePlatformDensity,
scrollbarTheme: ScrollbarThemeData(
Expand All @@ -27,6 +28,14 @@ class ThemeUtils {
);
}

static TextSelectionThemeData get _textSelectionTheme {
return const TextSelectionThemeData(
cursorColor: AppColor.primaryColor,
selectionHandleColor: AppColor.primaryColor,
selectionColor: AppColor.primarySelectedColor,
);
}

static AppBarTheme get _appBarTheme {
return const AppBarTheme(
color: Colors.white,
Expand Down
73 changes: 65 additions & 8 deletions core/lib/presentation/views/text/text_field_builder.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:flutter/material.dart';
import 'package:languagetool_textfield/languagetool_textfield.dart';

class TextFieldBuilder extends StatefulWidget {

Expand All @@ -10,7 +11,7 @@ class TextFieldBuilder extends StatefulWidget {
final TapRegionCallback? onTapOutside;
final TextStyle? textStyle;
final TextInputAction? textInputAction;
final InputDecoration? decoration;
final InputDecoration decoration;
final bool obscureText;
final int? maxLines;
final int? minLines;
Expand All @@ -24,7 +25,9 @@ class TextFieldBuilder extends StatefulWidget {
final bool autocorrect;
final TextDirection textDirection;
final bool readOnly;
final bool spellCheck;
final MouseCursor? mouseCursor;
final LanguageToolController? languageToolController;

const TextFieldBuilder({
super.key,
Expand All @@ -33,13 +36,15 @@ class TextFieldBuilder extends StatefulWidget {
this.obscureText = false,
this.autoFocus = false,
this.readOnly = false,
this.spellCheck = false,
this.textStyle = const TextStyle(color: AppColor.textFieldTextColor),
this.textDirection = TextDirection.ltr,
this.textInputAction,
this.decoration,
this.decoration = const InputDecoration(),
this.maxLines,
this.minLines,
this.controller,
this.languageToolController,
this.keyboardType,
this.focusNode,
this.fromValue,
Expand All @@ -49,30 +54,78 @@ class TextFieldBuilder extends StatefulWidget {
this.onTapOutside,
this.onTextChange,
this.onTextSubmitted,
});
}) : assert(spellCheck == true), assert(languageToolController != null);

@override
State<TextFieldBuilder> createState() => _TextFieldBuilderState();
}

class _TextFieldBuilderState extends State<TextFieldBuilder> {

late TextEditingController _controller;
TextEditingController? _controller;
LanguageToolController? _languageToolController;

late TextDirection _textDirection;

@override
void initState() {
super.initState();
if (widget.fromValue != null) {
_controller = TextEditingController.fromValue(TextEditingValue(text: widget.fromValue!));
if (widget.spellCheck) {
_languageToolController = widget.languageToolController ?? LanguageToolController(
delay: const Duration(milliseconds: 200)
);
if (widget.fromValue != null) {
_languageToolController?.value = TextEditingValue(text: widget.fromValue!);
}
} else {
_controller = widget.controller ?? TextEditingController();
if (widget.fromValue != null) {
_controller = TextEditingController.fromValue(TextEditingValue(text: widget.fromValue!));
} else {
_controller = widget.controller ?? TextEditingController();
}
}
_textDirection = widget.textDirection;
}

@override
Widget build(BuildContext context) {
if (widget.spellCheck) {
return LanguageToolTextField(
key: widget.key,
controller: _languageToolController ?? LanguageToolController(),
cursorColor: widget.cursorColor,
autocorrect: widget.autocorrect,
textInputAction: widget.textInputAction,
decoration: widget.decoration,
maxLines: widget.maxLines,
minLines: widget.minLines,
keyboardAppearance: widget.keyboardAppearance,
style: widget.textStyle,
keyboardType: widget.keyboardType,
autoFocus: widget.autoFocus,
focusNode: widget.focusNode,
alignCenter: false,
padding: null,
textDirection: _textDirection,
readOnly: widget.readOnly,
mouseCursor: widget.mouseCursor,
onTextChange: (value) {
widget.onTextChange?.call(value);
if (value.isNotEmpty) {
final directionByText = DirectionUtils.getDirectionByEndsText(value);
if (directionByText != _textDirection) {
setState(() {
_textDirection = directionByText;
});
}
}
},
onTextSubmitted: widget.onTextSubmitted,
onTap: widget.onTap,
onTapOutside: widget.onTapOutside,
);
}

return TextField(
key: widget.key,
controller: _controller,
Expand Down Expand Up @@ -111,7 +164,11 @@ class _TextFieldBuilderState extends State<TextFieldBuilder> {
@override
void dispose() {
if (widget.fromValue == null && widget.controller == null) {
_controller.dispose();
if (widget.spellCheck) {
_languageToolController?.dispose();
} else {
_controller?.dispose();
}
}
super.dispose();
}
Expand Down
17 changes: 17 additions & 0 deletions core/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.9.0"
languagetool_textfield:
dependency: "direct main"
description:
path: "."
ref: twake-supported
resolved-ref: ae95b4c49af0dbfb7161cf1256027048751160e5
url: "https://github.com/dab246/languagetool_textfield.git"
source: git
version: "0.1.0"
leak_tracker:
dependency: transitive
description:
Expand Down Expand Up @@ -981,6 +990,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
throttling:
dependency: transitive
description:
name: throttling
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
url: "https://pub.dev"
source: hosted
version: "2.0.1"
timing:
dependency: transitive
description:
Expand Down
8 changes: 8 additions & 0 deletions core/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ dependencies:
flutter:
sdk: flutter

### Dependencies from git ###
# TODO: We will change it when the PR in upstream repository will be merged
# https://github.com/solid-software/languagetool_textfield/pull/83
languagetool_textfield:
git:
url: https://github.com/dab246/languagetool_textfield.git
ref: twake-supported

### Dependencies from pub.dev ###
cupertino_icons: 1.0.6

Expand Down
6 changes: 5 additions & 1 deletion lib/features/composer/presentation/composer_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:jmap_dart_client/jmap/identities/identity.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/mailbox/mailbox.dart';
import 'package:languagetool_textfield/languagetool_textfield.dart';
import 'package:model/model.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
Expand Down Expand Up @@ -143,7 +144,9 @@ class ComposerController extends BaseController with DragDropFileMixin implement
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;

final subjectEmailInputController = TextEditingController();
final subjectEmailInputController = LanguageToolController(
delay: const Duration(milliseconds: 200),
);
final toEmailAddressController = TextEditingController();
final ccEmailAddressController = TextEditingController();
final bccEmailAddressController = TextEditingController();
Expand Down Expand Up @@ -1887,6 +1890,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement

void handleOnFocusHtmlEditorWeb() {
FocusManager.instance.primaryFocus?.unfocus();
subjectEmailInputController.popupWidget?.popupRenderer.dismiss();
richTextWebController?.editorController.setFocus();
richTextWebController?.closeAllMenuPopup();
}
Expand Down
6 changes: 3 additions & 3 deletions lib/features/composer/presentation/styles/composer_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ class ComposerStyle {
static const EdgeInsetsGeometry desktopRecipientPadding = EdgeInsetsDirectional.only(end: 24);
static const EdgeInsetsGeometry desktopRecipientMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry desktopSubjectMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry desktopSubjectPadding = EdgeInsetsDirectional.only(end: 24, top: 12, bottom: 12);
static const EdgeInsetsGeometry desktopSubjectPadding = EdgeInsetsDirectional.only(end: 24);
static const EdgeInsetsGeometry desktopEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 20);
static const EdgeInsetsGeometry tabletRecipientPadding = EdgeInsetsDirectional.only(end: 24);
static const EdgeInsetsGeometry tabletRecipientMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry tabletSubjectMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry tabletSubjectPadding = EdgeInsetsDirectional.only(end: 24, top: 12, bottom: 12);
static const EdgeInsetsGeometry tabletSubjectPadding = EdgeInsetsDirectional.only(end: 24);
static const EdgeInsetsGeometry tabletEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 20);
static const EdgeInsetsGeometry mobileRecipientPadding = EdgeInsetsDirectional.only(end: 16);
static const EdgeInsetsGeometry mobileRecipientMargin = EdgeInsetsDirectional.only(start: 16);
static const EdgeInsetsGeometry mobileSubjectMargin = EdgeInsetsDirectional.only(start: 16);
static const EdgeInsetsGeometry mobileSubjectPadding = EdgeInsetsDirectional.only(end: 16, top: 12, bottom: 12);
static const EdgeInsetsGeometry mobileSubjectPadding = EdgeInsetsDirectional.only(end: 16);
static const EdgeInsetsGeometry mobileEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 12);
static const EdgeInsetsGeometry popupItemPadding = EdgeInsetsDirectional.symmetric(horizontal: 12);
static const EdgeInsetsGeometry insertImageLoadingBarPadding = EdgeInsetsDirectional.only(top: 12);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'package:core/presentation/views/text/text_field_builder.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:flutter/material.dart';
import 'package:languagetool_textfield/languagetool_textfield.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/subject_composer_widget_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';

class SubjectComposerWidget extends StatelessWidget {

final FocusNode? focusNode;
final TextEditingController textController;
final LanguageToolController textController;
final ValueChanged<String>? onTextChange;
final EdgeInsetsGeometry? margin;
final EdgeInsetsGeometry? padding;
Expand Down Expand Up @@ -47,9 +48,11 @@ class SubjectComposerWidget extends StatelessWidget {
focusNode: focusNode,
onTextChange: onTextChange,
maxLines: 1,
spellCheck: true,
decoration: const InputDecoration(border: InputBorder.none),
textDirection: DirectionUtils.getDirectionByLanguage(context),
textStyle: SubjectComposerWidgetStyle.inputTextStyle,
controller: textController,
languageToolController: textController,
)
)
]
Expand Down
17 changes: 17 additions & 0 deletions model/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.6.1"
languagetool_textfield:
dependency: transitive
description:
path: "."
ref: twake-supported
resolved-ref: ae95b4c49af0dbfb7161cf1256027048751160e5
url: "https://github.com/dab246/languagetool_textfield.git"
source: git
version: "0.1.0"
leak_tracker:
dependency: transitive
description:
Expand Down Expand Up @@ -1013,6 +1022,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
throttling:
dependency: transitive
description:
name: throttling
sha256: e48a4c681b1838b8bf99c1a4f822efe43bb69132f9a56091cd5b7d931c862255
url: "https://pub.dev"
source: hosted
version: "2.0.1"
timing:
dependency: transitive
description:
Expand Down
Loading

0 comments on commit 4f1f8ec

Please sign in to comment.