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 11e47ecf2..408367fa6 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -84,16 +84,19 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/local_sort_o import 'package:tmail_ui_user/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/app_grid_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/linagora_ecosystem_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/search_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/app_grid_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/linagora_ecosystem_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/search_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/spam_report_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_grid_linagra_ecosystem_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_scribe_prompt_url_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_report_state_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_stored_email_sort_order_interactor.dart'; @@ -381,6 +384,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => EmptySpamFolderInteractor(Get.find())); Get.lazyPut(() => GetAppDashboardConfigurationInteractor(Get.find())); Get.lazyPut(() => GetAppGridLinagraEcosystemInteractor(Get.find())); + Get.lazyPut(() => GetScribePromptUrlInteractor(Get.find())); Get.lazyPut(() => GetEmailByIdInteractor( Get.find(), Get.find())); @@ -443,6 +447,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -495,5 +500,6 @@ class MailboxDashBoardBindings extends BaseBindings { DataSourceType.network: Get.find(), DataSourceType.local: Get.find() },)); + Get.lazyPut(() => LinagoraEcosystemRepositoryImpl(Get.find())); } } \ No newline at end of file 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 c635398e8..4249fce3b 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -106,6 +106,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_action import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_scribe_prompt_url_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_stored_email_sort_order_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_text_formatting_menu_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; @@ -124,6 +125,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller 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_ai_needs_action_setting_extension.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/ai_scribe/setup_scribe_prompt_url_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'; @@ -542,6 +544,8 @@ class MailboxDashBoardController extends ReloadableController updateTextFormattingMenuState(success.isDisplayed); } else if (success is GetAIScribeConfigSuccess) { handleLoadAIScribeConfigSuccess(success.aiScribeConfig); + } else if (success is GetScribePromptUrlSuccess) { + handleGetScribePromptUrlSuccess(success); } else { super.handleSuccessViewState(success); } @@ -592,6 +596,8 @@ class MailboxDashBoardController extends ReloadableController updateTextFormattingMenuState(false); } else if (failure is GetAIScribeConfigFailure) { handleLoadAIScribeConfigFailure(); + } else if (failure is GetScribePromptUrlFailure) { + handleGetScribePromptUrlFailure(failure); } else { super.handleFailureViewState(failure); } @@ -922,6 +928,8 @@ class MailboxDashBoardController extends ReloadableController } else { injectWebSocket(session: session, accountId: currentAccountId); } + + loadScribePromptUrl(); } void _handleMailtoURL(MailtoArguments arguments) { diff --git a/lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_scribe_prompt_url_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_scribe_prompt_url_extension.dart new file mode 100644 index 000000000..fe5f9c7d8 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/ai_scribe/setup_scribe_prompt_url_extension.dart @@ -0,0 +1,32 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:scribe/scribe/ai/domain/service/prompt_service.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_scribe_prompt_url_state.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_scribe_prompt_url_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +extension SetupScribePromptUrlExtension on MailboxDashBoardController { + void loadScribePromptUrl() { + final interactor = getBinding(); + + if (interactor != null) { + final baseUrl = dynamicUrlInterceptors.jmapUrl; + if (baseUrl != null && baseUrl.isNotEmpty) { + consumeState(interactor.execute(baseUrl)); + } else { + logError('SetupScribePromptUrlExtension::loadScribePromptUrl: jmapUrl is null or empty'); + } + } else { + logError('SetupScribePromptUrlExtension::loadScribePromptUrl: GetScribePromptUrlInteractor not found'); + } + } + + void handleGetScribePromptUrlSuccess(GetScribePromptUrlSuccess success) { + PromptService().setPromptUrl(success.promptUrl); + } + + void handleGetScribePromptUrlFailure(GetScribePromptUrlFailure failure) { + logError('SetupScribePromptUrlExtension::handleGetScribePromptUrlFailure: GetScribePromptUrl failed - ${failure.exception}'); + PromptService().setPromptUrl(null); + } +} diff --git a/scribe/lib/scribe.dart b/scribe/lib/scribe.dart index bd891d41c..24d81532e 100644 --- a/scribe/lib/scribe.dart +++ b/scribe/lib/scribe.dart @@ -4,6 +4,7 @@ export 'scribe/ai/data/datasource/ai_datasource.dart'; export 'scribe/ai/data/repository/ai_repository_impl.dart'; export 'scribe/ai/domain/model/ai_response.dart'; export 'scribe/ai/domain/repository/ai_scribe_repository.dart'; +export 'scribe/ai/domain/service/prompt_service.dart'; export 'scribe/ai/domain/state/generate_ai_text_state.dart'; export 'scribe/ai/domain/usecases/generate_ai_text_interactor.dart'; export 'scribe/ai/localizations/scribe_localizations.dart'; diff --git a/scribe/lib/scribe/ai/domain/service/prompt_service.dart b/scribe/lib/scribe/ai/domain/service/prompt_service.dart index 7bf1762a9..bb703ef07 100644 --- a/scribe/lib/scribe/ai/domain/service/prompt_service.dart +++ b/scribe/lib/scribe/ai/domain/service/prompt_service.dart @@ -1,22 +1,85 @@ import 'dart:convert'; -import 'package:flutter/services.dart' show rootBundle; +import 'package:core/data/network/dio_client.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:dio/dio.dart'; import 'package:scribe/scribe/ai/data/model/ai_message.dart'; import 'package:scribe/scribe/ai/domain/model/prompt_data.dart'; +import 'package:flutter/services.dart' show rootBundle; class PromptService { - static final PromptService _instance = PromptService._internal(); + static PromptService? _instance; + final DioClient _dioClient; - factory PromptService() => _instance; + factory PromptService({DioClient? dioClient}) { + _instance ??= PromptService._internal(dioClient: dioClient); + return _instance!; + } - PromptService._internal(); + PromptService._internal({DioClient? dioClient}) + : _dioClient = dioClient ?? DioClient(Dio(BaseOptions( + connectTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 10), + ))); PromptData? _promptData; + String? _promptUrl; + + void setPromptUrl(String? url) { + _promptUrl = url; + } + + String? get promptUrl => _promptUrl; Future loadPrompts() async { + // App in memory cache if (_promptData != null) { return _promptData!; } + // First try: remote prompts + if (_promptUrl != null) { + try { + _promptData = await _fetchPromptsFromUrl(_promptUrl!); + return _promptData!; + } catch (e) { + log('PromptService::loadPrompts: failed to fetch from remote URL: $e'); + } + } + + // Fallback: local prompts + return _loadPromptsFromAssets(); + } + + Future _fetchPromptsFromUrl(String url) async { + log('PromptService::_fetchPromptsFromUrl: Fetching from $url'); + + try { + final response = await _dioClient.get(url); + + if (response.statusCode == 200) { + final jsonData = response.data; + Map promptsMap; + + if (jsonData is String) { + promptsMap = jsonDecode(jsonData) as Map; + } else if (jsonData is Map) { + promptsMap = jsonData; + } else { + throw Exception('Failed to fetch prompts: invalid response format'); + } + + log('PromptService::_fetchPromptsFromUrl: Successfully fetched prompts'); + return PromptData.fromJson(promptsMap); + } else { + throw Exception('Failed to fetch prompts: unexpected status code ${response.statusCode}'); + } + } catch (e) { + log('PromptService::_fetchPromptsFromUrl: Exception: $e'); + rethrow; + } + } + + Future _loadPromptsFromAssets() async { try { final jsonString = await rootBundle.loadString('packages/scribe/assets/prompts.json'); final jsonData = jsonDecode(jsonString) as Map;