From 6e56a50713d4093e876587b76598ee47f2a92c48 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 19 Dec 2025 15:05:41 +0700 Subject: [PATCH] feat(ai-scribe): Load AI scribe config in dashboard --- lib/features/base/mixin/ai_scribe_mixin.dart | 13 ++------ .../presentation/composer_bindings.dart | 2 -- .../presentation/composer_controller.dart | 15 ---------- .../composer/presentation/composer_view.dart | 2 +- .../presentation/composer_view_web.dart | 2 +- ...andle_ai_scribe_in_composer_extension.dart | 8 ++++- .../bindings/mailbox_dashboard_bindings.dart | 7 +++++ .../mailbox_dashboard_controller.dart | 19 ++++++++++-- .../setup_cached_ai_scribe_extension.dart | 30 +++++++++++++++++++ .../datasource/manage_account_datasource.dart | 3 ++ .../manage_account_datasource_impl.dart | 7 +++++ .../local/preferences_setting_manager.dart | 2 -- .../manage_account_repository_impl.dart | 6 ++++ .../repository/manage_account_repository.dart | 3 ++ .../get_ai_scribe_config_interactor.dart | 5 ++-- .../preferences_interactors_bindings.dart | 4 --- .../ai/presentation/model/ai_capability.dart | 25 ++++++++++++++-- .../composer_controller_test.dart | 11 ++++--- 18 files changed, 114 insertions(+), 50 deletions(-) create mode 100644 lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_cached_ai_scribe_extension.dart diff --git a/lib/features/base/mixin/ai_scribe_mixin.dart b/lib/features/base/mixin/ai_scribe_mixin.dart index c02e35dc9..293267b5a 100644 --- a/lib/features/base/mixin/ai_scribe_mixin.dart +++ b/lib/features/base/mixin/ai_scribe_mixin.dart @@ -23,18 +23,9 @@ mixin AiScribeMixin { session: session, accountId: accountId, ); - final scribeEndpoint = aiCapability?.scribeEndpoint; - - if (scribeEndpoint == null || scribeEndpoint.isEmpty) return; - - // Validate endpoint format - if (Uri.tryParse(scribeEndpoint)?.hasAbsolutePath != true) { - logError( - 'AiScribeMixin::injectAIScribeBindings(): Invalid endpoint format: $scribeEndpoint'); - return; + if (aiCapability?.isScribeEndpointAvailable == true) { + AIScribeBindings(aiCapability!.scribeEndpoint!).dependencies(); } - - AIScribeBindings(scribeEndpoint).dependencies(); } catch (e) { logError('AiScribeMixin::injectAIScribeBindings(): $e'); } diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index 54cb30ca7..27cc6b6ad 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -54,7 +54,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/se import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/preferences/bindings/preferences_interactors_bindings.dart'; @@ -339,7 +338,6 @@ class ComposerBindings extends BaseBindings { Get.find(tag: composerId), Get.find(tag: composerId), Get.find(tag: composerId), - Get.find(tag: composerId), 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 8f0f3f120..e30528d12 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -107,10 +107,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_text_formatting_menu_state_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/validate_premium_storage_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/get_ai_scribe_config_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart'; import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart' @@ -164,7 +161,6 @@ class ComposerController extends BaseController final isMarkAsImportant = Rx(false); final isContentHeightExceeded = Rx(false); final editorTextSelection = Rxn(); - final _cachedAIScribeConfig = Rx(AIScribeConfig.initial()); final LocalFilePickerInteractor _localFilePickerInteractor; final LocalImagePickerInteractor _localImagePickerInteractor; @@ -183,7 +179,6 @@ class ComposerController extends BaseController final String? composerId; final ComposerArguments? composerArgs; final SaveTemplateEmailInteractor _saveTemplateEmailInteractor; - final GetAIScribeConfigInteractor _getAIScribeConfigInteractor; GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor; GetAutoCompleteInteractor? _getAutoCompleteInteractor; @@ -271,8 +266,6 @@ class ComposerController extends BaseController String get ownEmailAddress => mailboxDashBoardController.ownEmailAddress.value; - AIScribeConfig get aiScribeConfig => _cachedAIScribeConfig.value; - late Worker uploadInlineImageWorker; late bool _isEmailBodyLoaded; @@ -292,7 +285,6 @@ class ComposerController extends BaseController this.printEmailInteractor, this._composerRepository, this._saveTemplateEmailInteractor, - this._getAIScribeConfigInteractor, { this.composerId, this.composerArgs, @@ -316,11 +308,6 @@ class ComposerController extends BaseController _beforeReconnectManager.addListener(onBeforeReconnect); _injectBinding(); onKeyboardShortcutInit(); - _loadAIScribeConfig(); - } - - void _loadAIScribeConfig() { - consumeState(_getAIScribeConfigInteractor.execute()); } @override @@ -421,8 +408,6 @@ class ComposerController extends BaseController richTextMobileTabletController?.insertImage(inlineImage); } maxWithEditor = null; - } else if (success is GetAIScribeConfigSuccess) { - _cachedAIScribeConfig.value = success.aiScribeConfig; } else { super.handleSuccessViewState(success); } diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 556a512b3..69f262cdd 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -470,7 +470,7 @@ class ComposerView extends GetWidget { sendMessageAction: () => controller.handleClickSendButton(context), requestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), - onOpenAiAssistantModal: controller.aiScribeConfig.isEnabled && controller.isAIScribeAvailable + onOpenAiAssistantModal: controller.isAIScribeAvailable ? controller.openAIAssistantModal : null, )), diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 65653bc22..a2db486c3 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -560,7 +560,7 @@ class ComposerView extends GetWidget { toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), onOpenInsertLink: controller.openInsertLink, - onOpenAiAssistantModal: controller.isAIScribeAvailable && controller.aiScribeConfig.isEnabled + onOpenAiAssistantModal: controller.isAIScribeAvailable ? controller.openAIAssistantModal : null, )), 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 8ed485b32..a9a872215 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 @@ -13,7 +13,13 @@ extension HandleAiScribeInComposerExtension on ComposerController { session: mailboxDashBoardController.sessionCurrent, accountId: mailboxDashBoardController.accountId.value, ); - return aiCapability?.isScribeEndpointAvailable == true; + final isScribeEndpointAvailable = + aiCapability?.isScribeEndpointAvailable == true; + + final isAIScribeConfigEnabled = + mailboxDashBoardController.cachedAIScribeConfig.value.isEnabled; + + return isAIScribeConfigEnabled && isScribeEndpointAvailable; } Future _getTextOnlyContentInEditor() async { diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index 8d3604099..c4886dd99 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -4,6 +4,7 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:core/utils/config/app_config_loader.dart'; import 'package:core/utils/file_utils.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:core/utils/preview_eml_file_utils.dart'; import 'package:core/utils/print_utils.dart'; import 'package:get/get.dart'; @@ -114,6 +115,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/manage_account/data/local/preferences_setting_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_account_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/bindings/setting_interactor_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; @@ -417,6 +419,11 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), ), ); + if (!PlatformInfo.isMobile) { + Get.lazyPut( + () => GetAIScribeConfigInteractor(Get.find()), + ); + } } @override diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 5fead10f9..d9b18172f 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -120,6 +120,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dow import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_cached_ai_scribe_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/cleanup_recent_search_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/delete_emails_in_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_type_for_email_selection.dart'; @@ -148,11 +149,14 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/sear import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/mailto/presentation/model/mailto_arguments.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_rule_filter_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/get_ai_scribe_config_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_vacation_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacation_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_vacation_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/save_language_interactor.dart'; @@ -273,6 +277,7 @@ class MailboxDashBoardController extends ReloadableController SaveLanguageInteractor? saveLanguageInteractor; GetTextFormattingMenuStateInteractor? getTextFormattingMenuStateInteractor; SaveTextFormattingMenuStateInteractor? saveTextFormattingMenuStateInteractor; + GetAIScribeConfigInteractor? getAIScribeConfigInteractor; final scaffoldKey = GlobalKey(); final selectedMailbox = Rxn(); @@ -304,6 +309,7 @@ class MailboxDashBoardController extends ReloadableController final isPopupMenuOpened = RxBool(false); final octetsQuota = Rxn(); final isTextFormattingMenuOpened = RxBool(false); + final cachedAIScribeConfig = Rx(AIScribeConfig.initial()); Map mapDefaultMailboxIdByRole = {}; Map mapMailboxById = {}; @@ -399,6 +405,7 @@ class MailboxDashBoardController extends ReloadableController } _handleArguments(); _loadAppGrid(); + loadAIScribeConfig(); super.onReady(); } @@ -524,6 +531,8 @@ class MailboxDashBoardController extends ReloadableController setUpDefaultEmailSortOrder(success.emailSortOrderType); } else if (success is GetTextFormattingMenuStateSuccess) { updateTextFormattingMenuState(success.isDisplayed); + } else if (success is GetAIScribeConfigSuccess) { + handleLoadAIScribeConfigSuccess(success.aiScribeConfig); } else { super.handleSuccessViewState(success); } @@ -571,6 +580,8 @@ class MailboxDashBoardController extends ReloadableController backToHomeScreen(); } else if (failure is GetTextFormattingMenuStateFailure) { updateTextFormattingMenuState(false); + } else if (failure is GetAIScribeConfigFailure) { + handleLoadAIScribeConfigFailure(); } else { super.handleFailureViewState(failure); } @@ -2047,6 +2058,7 @@ class MailboxDashBoardController extends ReloadableController notifyThreadDetailSettingUpdated(); getServerSetting(); spamReportController.getSpamReportStateAction(); + loadAIScribeConfig(); } Future> quickSearchEmails(String query) async { @@ -2123,6 +2135,7 @@ class MailboxDashBoardController extends ReloadableController notifyThreadDetailSettingUpdated(); getServerSetting(); spamReportController.getSpamReportStateAction(); + loadAIScribeConfig(); } void _handleUpdateVacationSuccess(UpdateVacationSuccess success) { @@ -2451,7 +2464,7 @@ class MailboxDashBoardController extends ReloadableController void updateEmailList(List newEmailList) { emailsInCurrentMailbox.value = newEmailList; } - + void openMailboxAction(PresentationMailbox presentationMailbox) { dispatchMailboxUIAction(OpenMailboxAction(presentationMailbox)); } @@ -3217,7 +3230,7 @@ class MailboxDashBoardController extends ReloadableController leadingSVGIcon: imagePaths.icRecoverDeletedMessages, leadingSVGIconColor: Colors.white, backgroundColor: AppColor.primaryColor, - textColor: Colors.white, + textColor: Colors.white, ); } } @@ -3229,7 +3242,7 @@ class MailboxDashBoardController extends ReloadableController if (currentAccountId != null && currentSession != null) { final arguments = EmailRecoveryArguments(currentAccountId, currentSession); - final result = PlatformInfo.isWeb + final result = PlatformInfo.isWeb ? await DialogRouter.pushGeneralDialog( routeName: AppRoutes.emailRecovery, arguments: arguments, diff --git a/lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_cached_ai_scribe_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_cached_ai_scribe_extension.dart new file mode 100644 index 000000000..592d17d04 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_cached_ai_scribe_extension.dart @@ -0,0 +1,30 @@ +import 'package:core/utils/platform_info.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +extension SetupCachedAiScribeExtension on MailboxDashBoardController { + void loadAIScribeConfig() { + if (PlatformInfo.isMobile) { + cachedAIScribeConfig.value = AIScribeConfig(isEnabled: false); + return; + } + + getAIScribeConfigInteractor = getBinding(); + + if (getAIScribeConfigInteractor != null) { + consumeState(getAIScribeConfigInteractor!.execute()); + } else { + handleLoadAIScribeConfigFailure(); + } + } + + void handleLoadAIScribeConfigSuccess(AIScribeConfig aiScribeConfig) { + cachedAIScribeConfig.value = aiScribeConfig; + } + + void handleLoadAIScribeConfigFailure() { + cachedAIScribeConfig.value = AIScribeConfig.initial(); + } +} diff --git a/lib/features/manage_account/data/datasource/manage_account_datasource.dart b/lib/features/manage_account/data/datasource/manage_account_datasource.dart index 7591877a9..b0888e3f4 100644 --- a/lib/features/manage_account/data/datasource/manage_account_datasource.dart +++ b/lib/features/manage_account/data/datasource/manage_account_datasource.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/preferences_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/preferences_setting.dart'; @@ -9,4 +10,6 @@ abstract class ManageAccountDataSource { Future toggleLocalSettingsState(PreferencesConfig preferencesConfig); Future getLocalSettings(); + + Future getAiScribeConfigLocalSettings(); } diff --git a/lib/features/manage_account/data/datasource_impl/manage_account_datasource_impl.dart b/lib/features/manage_account/data/datasource_impl/manage_account_datasource_impl.dart index 45db1a916..c0f2b088b 100644 --- a/lib/features/manage_account/data/datasource_impl/manage_account_datasource_impl.dart +++ b/lib/features/manage_account/data/datasource_impl/manage_account_datasource_impl.dart @@ -64,4 +64,11 @@ class ManageAccountDataSourceImpl extends ManageAccountDataSource { return await _preferencesSettingManager.loadPreferences(); }).catchError(_exceptionThrower.throwException); } + + @override + Future getAiScribeConfigLocalSettings() { + return Future.sync(() async { + return await _preferencesSettingManager.getAIScribeConfig(); + }).catchError(_exceptionThrower.throwException); + } } \ No newline at end of file diff --git a/lib/features/manage_account/data/local/preferences_setting_manager.dart b/lib/features/manage_account/data/local/preferences_setting_manager.dart index 42024bf56..5a7269411 100644 --- a/lib/features/manage_account/data/local/preferences_setting_manager.dart +++ b/lib/features/manage_account/data/local/preferences_setting_manager.dart @@ -166,6 +166,4 @@ class PreferencesSettingManager { final updatedConfig = currentConfig.copyWith(isEnabled: isEnabled); await savePreferences(updatedConfig); } - - } diff --git a/lib/features/manage_account/data/repository/manage_account_repository_impl.dart b/lib/features/manage_account/data/repository/manage_account_repository_impl.dart index bb5fbd0b3..9f8d75d9e 100644 --- a/lib/features/manage_account/data/repository/manage_account_repository_impl.dart +++ b/lib/features/manage_account/data/repository/manage_account_repository_impl.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:tmail_ui_user/features/manage_account/data/datasource/manage_account_datasource.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/preferences_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/preferences_setting.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_account_repository.dart'; @@ -25,4 +26,9 @@ class ManageAccountRepositoryImpl extends ManageAccountRepository { Future getLocalSettings() { return dataSource.getLocalSettings(); } + + @override + Future getAiScribeConfigLocalSettings() { + return dataSource.getAiScribeConfigLocalSettings(); + } } \ No newline at end of file diff --git a/lib/features/manage_account/domain/repository/manage_account_repository.dart b/lib/features/manage_account/domain/repository/manage_account_repository.dart index f96ed179f..ac5a75839 100644 --- a/lib/features/manage_account/domain/repository/manage_account_repository.dart +++ b/lib/features/manage_account/domain/repository/manage_account_repository.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/preferences_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/preferences_setting.dart'; @@ -9,4 +10,6 @@ abstract class ManageAccountRepository { Future toggleLocalSettingsState(PreferencesConfig preferencesConfig); Future getLocalSettings(); + + Future getAiScribeConfigLocalSettings(); } diff --git a/lib/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart b/lib/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart index 82d7d3659..37854a942 100644 --- a/lib/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart +++ b/lib/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart @@ -12,8 +12,9 @@ class GetAIScribeConfigInteractor { Stream> execute() async* { try { yield Right(GettingAIScribeConfigState()); - final preferencesSetting = await _manageAccountRepository.getLocalSettings(); - yield Right(GetAIScribeConfigSuccess(preferencesSetting.aiScribeConfig)); + final aiScribeConfig = + await _manageAccountRepository.getAiScribeConfigLocalSettings(); + yield Right(GetAIScribeConfigSuccess(aiScribeConfig)); } catch (e) { yield Left(GetAIScribeConfigFailure(exception: e)); } diff --git a/lib/features/manage_account/presentation/preferences/bindings/preferences_interactors_bindings.dart b/lib/features/manage_account/presentation/preferences/bindings/preferences_interactors_bindings.dart index bd6c5ff94..8994a9cfd 100644 --- a/lib/features/manage_account/presentation/preferences/bindings/preferences_interactors_bindings.dart +++ b/lib/features/manage_account/presentation/preferences/bindings/preferences_interactors_bindings.dart @@ -63,10 +63,6 @@ class PreferencesInteractorsBindings extends InteractorsBindings { () => SaveLanguageInteractor(Get.find(tag: composerId)), tag: composerId, ); - Get.lazyPut( - () => GetAIScribeConfigInteractor(Get.find(tag: composerId)), - tag: composerId, - ); } @override diff --git a/scribe/lib/scribe/ai/presentation/model/ai_capability.dart b/scribe/lib/scribe/ai/presentation/model/ai_capability.dart index 334c8395b..748589ebf 100644 --- a/scribe/lib/scribe/ai/presentation/model/ai_capability.dart +++ b/scribe/lib/scribe/ai/presentation/model/ai_capability.dart @@ -1,3 +1,4 @@ +import 'package:core/utils/app_logger.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -14,8 +15,28 @@ class AICapability extends CapabilityProperties { Map toJson() => _$AICapabilityToJson(this); - bool get isScribeEndpointAvailable => - scribeEndpoint?.trim().isNotEmpty == true; + bool get isScribeEndpointAvailable { + try { + final urlEndpoint = scribeEndpoint?.trim() ?? ''; + + if (urlEndpoint.isEmpty) return false; + + // Validate endpoint format - must be an absolute URI + if (Uri.tryParse(urlEndpoint)?.isAbsolute != true) { + logError( + 'AICapability::isScribeEndpointAvailable(): Invalid endpoint format: $urlEndpoint', + ); + return false; + } + + return true; + } catch (e) { + logError( + 'AICapability::isScribeEndpointAvailable(): Exception: $e', + ); + return false; + } + } @override List get props => [scribeEndpoint]; diff --git a/test/features/composer/presentation/composer_controller_test.dart b/test/features/composer/presentation/composer_controller_test.dart index 2a258e76c..d47551ca9 100644 --- a/test/features/composer/presentation/composer_controller_test.dart +++ b/test/features/composer/presentation/composer_controller_test.dart @@ -25,6 +25,7 @@ import 'package:model/email/email_action_type.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; +import 'package:scribe/scribe/ai/domain/usecases/generate_ai_text_interactor.dart'; import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart'; @@ -54,7 +55,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_ai_scribe_config_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/preferences/ai_scribe_config.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart'; @@ -69,7 +70,6 @@ import 'package:tmail_ui_user/main/utils/app_config.dart'; import 'package:tmail_ui_user/main/utils/toast_manager.dart'; import 'package:tmail_ui_user/main/utils/twake_app_manager.dart'; import 'package:uuid/uuid.dart'; -import 'package:scribe/scribe/ai/domain/usecases/generate_ai_text_interactor.dart'; import '../../../fixtures/account_fixtures.dart'; import '../../../fixtures/session_fixtures.dart'; @@ -145,6 +145,9 @@ class MockMailboxDashBoardController extends Mock implements MailboxDashBoardCon @override RxBool get isPopupMenuOpened => false.obs; + + @override + Rx get cachedAIScribeConfig => Rx(AIScribeConfig.initial()); } @GenerateNiceMocks([ @@ -181,7 +184,6 @@ class MockMailboxDashBoardController extends Mock implements MailboxDashBoardCon MockSpec(), MockSpec(), MockSpec(), - MockSpec(), // Additional Getx dependencies mock specs MockSpec(fallbackGenerators: fallbackGenerators), @@ -227,7 +229,6 @@ void main() { late MockPrintEmailInteractor mockPrintEmailInteractor; late MockComposerRepository mockComposerRepository; late MockSaveTemplateEmailInteractor mockSaveTemplateEmailInteractor; - late MockGetAIScribeConfigInteractor mockGetAIScribeConfigInteractor; // Declaration Getx dependencies final mockMailboxDashBoardController = MockMailboxDashBoardController(); @@ -299,7 +300,6 @@ void main() { mockPrintEmailInteractor = MockPrintEmailInteractor(); mockComposerRepository = MockComposerRepository(); mockSaveTemplateEmailInteractor = MockSaveTemplateEmailInteractor(); - mockGetAIScribeConfigInteractor = MockGetAIScribeConfigInteractor(); composerController = ComposerController( mockLocalFilePickerInteractor, @@ -317,7 +317,6 @@ void main() { mockPrintEmailInteractor, mockComposerRepository, mockSaveTemplateEmailInteractor, - mockGetAIScribeConfigInteractor, ); mockHtmlEditorApi = MockHtmlEditorApi();