diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index 8647672ee..27cc6b6ad 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -74,7 +74,6 @@ import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart'; import 'package:uuid/uuid.dart'; -import 'package:scribe/scribe/ai/domain/usecases/generate_ai_text_interactor.dart'; class ComposerBindings extends BaseBindings { @@ -339,7 +338,6 @@ class ComposerBindings extends BaseBindings { Get.find(tag: composerId), Get.find(tag: composerId), Get.find(tag: composerId), - Get.find(), composerId: composerId, composerArgs: composerArguments, ), tag: composerId); diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index f59ea43e9..dae80a8d9 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -27,7 +27,6 @@ import 'package:model/model.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; -import 'package:scribe/scribe/ai/presentation/widgets/ai_scribe.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/before_reconnect_handler.dart'; @@ -78,6 +77,7 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_em import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_list_identities_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_selected_identity_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/update_screen_display_mode_extension.dart'; +import 'package:tmail_ui_user/features/composer/presentation/mixin/ai_scribe_in_composer_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/drag_drog_file_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart'; @@ -86,7 +86,6 @@ import 'package:tmail_ui_user/features/composer/presentation/model/saved_composi import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/mixins/text_selection_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/from_composer_bottom_sheet_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/saving_message_dialog_view.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/saving_template_dialog_view.dart'; @@ -132,7 +131,7 @@ import 'package:tmail_ui_user/main/universal_import/html_stub.dart' as html; import 'package:tmail_ui_user/main/utils/app_config.dart'; class ComposerController extends BaseController - with DragDropFileMixin, AutoCompleteResultMixin, EditorViewMixin + with DragDropFileMixin, AutoCompleteResultMixin, EditorViewMixin, AIScribeInComposerMixin implements BeforeReconnectHandler { final mailboxDashBoardController = Get.find(); @@ -159,8 +158,6 @@ class ComposerController extends BaseController final isMarkAsImportant = Rx(false); final isContentHeightExceeded = Rx(false); - final editorTextSelection = Rxn(); - final LocalFilePickerInteractor _localFilePickerInteractor; final LocalImagePickerInteractor _localImagePickerInteractor; final GetEmailContentInteractor _getEmailContentInteractor; @@ -178,7 +175,6 @@ class ComposerController extends BaseController final String? composerId; final ComposerArguments? composerArgs; final SaveTemplateEmailInteractor _saveTemplateEmailInteractor; - final GenerateAITextInteractor _generateAITextInteractor; GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor; GetAutoCompleteInteractor? _getAutoCompleteInteractor; @@ -204,7 +200,6 @@ class ComposerController extends BaseController final GlobalKey keyReplyToEmailTagEditor = GlobalKey(); final GlobalKey headerEditorMobileWidgetKey = GlobalKey(); final GlobalKey identityDropdownKey = GlobalKey(); - final GlobalKey aiScribeButtonKey = GlobalKey(); final double defaultPaddingCoordinateYCursorEditor = 8; FocusNode? subjectEmailInputFocusNode; @@ -226,8 +221,11 @@ class ComposerController extends BaseController StreamSubscription? _subscriptionOnBlur; StreamSubscription? _composerCacheListener; + @override RichTextMobileTabletController? richTextMobileTabletController; + @override RichTextWebController? richTextWebController; + CustomPopupMenuController? menuMoreOptionController; final ScrollController scrollController = ScrollController(); @@ -264,6 +262,9 @@ class ComposerController extends BaseController TransformHtmlEmailContentInteractor get transformHtmlEmailContentInteractor => _transformHtmlEmailContentInteractor; + @override + GenerateAITextInteractor get generateAITextInteractor => Get.find(); + String get ownEmailAddress => mailboxDashBoardController.ownEmailAddress.value; @@ -286,7 +287,6 @@ class ComposerController extends BaseController this.printEmailInteractor, this._composerRepository, this._saveTemplateEmailInteractor, - this._generateAITextInteractor, { this.composerId, this.composerArgs, @@ -867,6 +867,7 @@ class ComposerController extends BaseController } } + @override Future getTextOnlyContentInEditor() async { try { final htmlContent = await getContentInEditor(); @@ -880,69 +881,6 @@ class ComposerController extends BaseController } } - void insertTextInEditor(String text) { - final htmlContent = text.replaceAll('\n', '
'); - - if (PlatformInfo.isWeb) { - richTextWebController?.editorController.insertHtml(htmlContent); - } else { - richTextMobileTabletController?.htmlEditorApi?.insertHtml(htmlContent); - } - } - - void showAIScribeMenuForFullText(BuildContext context) async { - final fullText = await getTextOnlyContentInEditor(); - - final RenderBox? renderBox = aiScribeButtonKey.currentContext?.findRenderObject() as RenderBox?; - Offset? buttonPosition; - if (renderBox != null) { - buttonPosition = renderBox.localToGlobal(Offset.zero); - } - - if (!context.mounted) return; - - showAIScribeDialog( - context: context, - imagePaths: imagePaths, - content: fullText, - onInsertText: insertTextInEditor, - interactor: _generateAITextInteractor, - buttonPosition: buttonPosition, - ); - } - - void showAIScribeMenuForSelectedText(BuildContext context, {Offset? buttonPosition}) { - final selection = editorTextSelection.value?.selectedText; - if (selection == null || selection.isEmpty) { - return; - } - - showAIScribeDialog( - context: context, - imagePaths: imagePaths, - content: selection, - onInsertText: insertTextInEditor, - interactor: _generateAITextInteractor, - buttonPosition: buttonPosition, - ); - } - - void handleTextSelection(TextSelectionData? textSelectionData) { - if (textSelectionData != null && textSelectionData.hasSelection) { - editorTextSelection.value = EditorTextSelection( - selectedText: textSelectionData.selectedText, - coordinates: textSelectionData.coordinates != null - ? Offset( - textSelectionData.coordinates!.x, - textSelectionData.coordinates!.y, - ) - : null, - ); - } else { - editorTextSelection.value = null; - } - } - Future _prepareToSendMessages(BuildContext context) async { final arguments = composerArguments.value; final session = mailboxDashBoardController.sessionCurrent; diff --git a/lib/features/composer/presentation/mixin/ai_scribe_in_composer_mixin.dart b/lib/features/composer/presentation/mixin/ai_scribe_in_composer_mixin.dart new file mode 100644 index 000000000..ea345299d --- /dev/null +++ b/lib/features/composer/presentation/mixin/ai_scribe_in_composer_mixin.dart @@ -0,0 +1,84 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:scribe/scribe.dart'; +import 'package:scribe/scribe/ai/presentation/widgets/ai_scribe.dart'; +import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart'; +import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/mixins/text_selection_mixin.dart'; + +mixin AIScribeInComposerMixin { + final editorTextSelection = Rxn(); + final GlobalKey aiScribeButtonKey = GlobalKey(); + + RichTextWebController? get richTextWebController; + RichTextMobileTabletController? get richTextMobileTabletController; + ImagePaths get imagePaths; + GenerateAITextInteractor get generateAITextInteractor; + + Future getTextOnlyContentInEditor(); + + void insertTextInEditor(String text) { + final htmlContent = text.replaceAll('\n', '
'); + + if (PlatformInfo.isWeb) { + richTextWebController?.editorController.insertHtml(htmlContent); + } else { + richTextMobileTabletController?.htmlEditorApi?.insertHtml(htmlContent); + } + } + + void showAIScribeMenuForFullText(BuildContext context) async { + final fullText = await getTextOnlyContentInEditor(); + + final RenderBox? renderBox = aiScribeButtonKey.currentContext?.findRenderObject() as RenderBox?; + Offset? buttonPosition; + if (renderBox != null) { + buttonPosition = renderBox.localToGlobal(Offset.zero); + } + + if (!context.mounted) return; + + showAIScribeDialog( + context: context, + imagePaths: imagePaths, + content: fullText, + onInsertText: insertTextInEditor, + interactor: generateAITextInteractor, + buttonPosition: buttonPosition, + ); + } + + void showAIScribeMenuForSelectedText(BuildContext context, {Offset? buttonPosition}) { + final selection = editorTextSelection.value?.selectedText; + if (selection == null || selection.isEmpty) { + return; + } + + showAIScribeDialog( + context: context, + imagePaths: imagePaths, + content: selection, + onInsertText: insertTextInEditor, + interactor: generateAITextInteractor, + buttonPosition: buttonPosition, + ); + } + + void handleTextSelection(TextSelectionData? textSelectionData) { + if (textSelectionData != null && textSelectionData.hasSelection) { + editorTextSelection.value = EditorTextSelection( + selectedText: textSelectionData.selectedText, + coordinates: textSelectionData.coordinates != null + ? Offset( + textSelectionData.coordinates!.x, + textSelectionData.coordinates!.y, + ) + : null, + ); + } else { + editorTextSelection.value = null; + } + } +} diff --git a/test/features/composer/presentation/composer_controller_test.dart b/test/features/composer/presentation/composer_controller_test.dart index 6965b366b..aee34137f 100644 --- a/test/features/composer/presentation/composer_controller_test.dart +++ b/test/features/composer/presentation/composer_controller_test.dart @@ -225,7 +225,6 @@ void main() { late MockPrintEmailInteractor mockPrintEmailInteractor; late MockComposerRepository mockComposerRepository; late MockSaveTemplateEmailInteractor mockSaveTemplateEmailInteractor; - late MockGenerateAITextInteractor mockGenerateAITextInteractor; // Declaration Getx dependencies final mockMailboxDashBoardController = MockMailboxDashBoardController(); @@ -296,7 +295,6 @@ void main() { mockPrintEmailInteractor = MockPrintEmailInteractor(); mockComposerRepository = MockComposerRepository(); mockSaveTemplateEmailInteractor = MockSaveTemplateEmailInteractor(); - mockGenerateAITextInteractor = MockGenerateAITextInteractor(); composerController = ComposerController( mockLocalFilePickerInteractor, @@ -314,7 +312,6 @@ void main() { mockPrintEmailInteractor, mockComposerRepository, mockSaveTemplateEmailInteractor, - mockGenerateAITextInteractor, ); mockHtmlEditorApi = MockHtmlEditorApi();