From 7a6a49fc7b3df6401906f6120270d7bd5c18bb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Poizat?= Date: Mon, 26 Jan 2026 16:07:40 +0100 Subject: [PATCH] Display Scribe mobile (bottomsheet) only when responsive mobile Previosuly, we display Scribe mobile (bottomsheet) if platform was mobile, so also Android or iOS tablet. Now we choose to display Scribe mobile relatively to display screens instead of platform to display the desktop Scribe (context menu) for tablets. --- .../handle_ai_scribe_in_composer_extension.dart | 11 +++++++++-- .../composer_ai_scribe_selection_overlay.dart | 3 +-- scribe/lib/scribe.dart | 1 + .../ai/presentation/utils/ai_scribe_mobile_utils.dart | 8 ++++++++ .../utils/modal/ai_scribe_modal_manager.dart | 8 +++++--- .../widgets/button/inline_ai_assist_button.dart | 5 +++++ 6 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 scribe/lib/scribe/ai/presentation/utils/ai_scribe_mobile_utils.dart diff --git a/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart b/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart index 5f157fd24..037e973c0 100644 --- a/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart +++ b/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart @@ -3,6 +3,7 @@ import 'package:core/utils/html/html_utils.dart'; import 'package:core/utils/platform_info.dart'; import 'package:core/utils/string_convert.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:scribe/scribe.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/text_selection_mixin.dart'; @@ -21,6 +22,11 @@ extension HandleAiScribeInComposerExtension on ComposerController { return isAIScribeConfigEnabled && isAIScribeEndpointAvailable; } + bool get isScribeMobile { + final context = Get.context; + return AiScribeMobileUtils.isScribeInMobileMode(context); + } + Future _getTextOnlyContentInEditor() async { try { final htmlContent = await getContentInEditor(); @@ -207,7 +213,7 @@ extension HandleAiScribeInComposerExtension on ComposerController { Future onReplaceTextCallback(String text) async { final selection = editorTextSelection.value?.selectedText; - final savedSelection = PlatformInfo.isMobile ? await getSavedSelection() : ""; + final savedSelection = isScribeMobile ? await getSavedSelection() : ""; final shouldReplaceEverything = (selection == null || selection.isEmpty) && savedSelection.isEmpty; @@ -232,7 +238,7 @@ extension HandleAiScribeInComposerExtension on ComposerController { clearFocusRecipients(); clearFocusSubject(); - if (PlatformInfo.isMobile) { + if (isScribeMobile) { await saveAndUnfocusForModal(); } @@ -247,6 +253,7 @@ extension HandleAiScribeInComposerExtension on ComposerController { preferredPlacement: ModalPlacement.top, crossAxisAlignment: ModalCrossAxisAlignment.start, onSelectAiScribeSuggestionAction: handleAiScribeSuggestionAction, + isScribeMobile: isScribeMobile, ); } diff --git a/lib/features/composer/presentation/widgets/ai_scribe/composer_ai_scribe_selection_overlay.dart b/lib/features/composer/presentation/widgets/ai_scribe/composer_ai_scribe_selection_overlay.dart index 46b072066..93da1828b 100644 --- a/lib/features/composer/presentation/widgets/ai_scribe/composer_ai_scribe_selection_overlay.dart +++ b/lib/features/composer/presentation/widgets/ai_scribe/composer_ai_scribe_selection_overlay.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:core/utils/platform_info.dart'; import 'package:scribe/scribe/ai/presentation/widgets/overlay/ai_selection_overlay.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart'; @@ -34,7 +33,7 @@ class ComposerAiScribeSelectionOverlay extends StatelessWidget { controller.clearFocusRecipients(); controller.clearFocusSubject(); - if (PlatformInfo.isMobile) { + if (controller.isScribeMobile) { await controller.saveAndUnfocusForModal(); } } diff --git a/scribe/lib/scribe.dart b/scribe/lib/scribe.dart index a9fed6417..bc420893b 100644 --- a/scribe/lib/scribe.dart +++ b/scribe/lib/scribe.dart @@ -22,6 +22,7 @@ export 'scribe/ai/presentation/model/modal/modal_placement.dart'; export 'scribe/ai/presentation/model/text_selection_model.dart'; export 'scribe/ai/presentation/styles/ai_scribe_styles.dart'; export 'scribe/ai/presentation/utils/ai_scribe_constants.dart'; +export 'scribe/ai/presentation/utils/ai_scribe_mobile_utils.dart'; export 'scribe/ai/presentation/utils/context_menu/hover_submenu_controller.dart'; export 'scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart'; export 'scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart'; diff --git a/scribe/lib/scribe/ai/presentation/utils/ai_scribe_mobile_utils.dart b/scribe/lib/scribe/ai/presentation/utils/ai_scribe_mobile_utils.dart new file mode 100644 index 000000000..2dc2c716e --- /dev/null +++ b/scribe/lib/scribe/ai/presentation/utils/ai_scribe_mobile_utils.dart @@ -0,0 +1,8 @@ +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:flutter/material.dart'; + +class AiScribeMobileUtils { + static bool isScribeInMobileMode(BuildContext? context) { + return context != null && ResponsiveUtils().isMobile(context); + } +} \ No newline at end of file diff --git a/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart b/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart index 8d421b6ff..677ec68ff 100644 --- a/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart +++ b/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart @@ -1,5 +1,4 @@ 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'; @@ -8,6 +7,7 @@ class AiScribeModalManager { AiScribeModalManager._(); static Future showAIScribeMenuModal({ + required bool isScribeMobile, required ImagePaths imagePaths, required List availableCategories, required OnSelectAiScribeSuggestionAction onSelectAiScribeSuggestionAction, @@ -19,7 +19,7 @@ class AiScribeModalManager { }) async { final AIAction? aiAction; - if (PlatformInfo.isMobile) { + if (isScribeMobile) { aiAction = await showMobileAIScribeMenuModal( imagePaths: imagePaths, content: content, @@ -46,6 +46,7 @@ class AiScribeModalManager { if (aiAction != null) { await showAIScribeSuggestionModal( aiAction: aiAction, + isScribeMobile: isScribeMobile, imagePaths: imagePaths, content: content, buttonPosition: buttonPosition, @@ -59,6 +60,7 @@ class AiScribeModalManager { static Future showAIScribeSuggestionModal({ required AIAction aiAction, + required bool isScribeMobile, required ImagePaths imagePaths, required OnSelectAiScribeSuggestionAction onSelectAiScribeSuggestionAction, String? content, @@ -67,7 +69,7 @@ class AiScribeModalManager { ModalPlacement? preferredPlacement, ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center, }) async { - if (PlatformInfo.isMobile) { + if (isScribeMobile) { await showMobileAIScribeSuggestionModal( aiAction: aiAction, imagePaths: imagePaths, diff --git a/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart b/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart index 2ec3fa582..ee4c71222 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart @@ -45,7 +45,12 @@ class InlineAiAssistButton extends StatelessWidget { await onTapFallback?.call(); + if (!context.mounted) return; + + final isScribeMobile = AiScribeMobileUtils.isScribeInMobileMode(context); + await AiScribeModalManager.showAIScribeMenuModal( + isScribeMobile: isScribeMobile, imagePaths: imagePaths, availableCategories: AIScribeMenuCategory.values, buttonPosition: position,