From 3124bccfeb84342be5570d1e43187a07f4507c5b Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:22:48 +0530 Subject: [PATCH] refactor: more logs --- lib/core/auth/auth_state_manager.dart | 55 +- lib/core/auth/token_validator.dart | 17 +- lib/core/error/api_error_handler.dart | 38 +- lib/core/error/api_error_interceptor.dart | 119 ++-- lib/core/error/enhanced_error_service.dart | 32 +- lib/core/error/error_parser.dart | 10 +- lib/core/services/api_service.dart | 436 ++++++------ .../services/attachment_upload_queue.dart | 23 +- .../services/optimized_storage_service.dart | 121 +++- lib/core/services/share_receiver_service.dart | 22 +- lib/core/services/socket_service.dart | 20 +- lib/core/services/storage_service.dart | 113 ++-- lib/core/services/streaming_helper.dart | 12 +- .../services/user_friendly_error_handler.dart | 14 +- .../chat/providers/chat_providers.dart | 27 +- lib/features/chat/views/chat_page.dart | 58 +- .../widgets/assistant_message_widget.dart | 627 +++++++++--------- .../widgets/enhanced_image_attachment.dart | 12 +- lib/main.dart | 13 +- lib/shared/services/tasks/task_queue.dart | 14 +- 20 files changed, 937 insertions(+), 846 deletions(-) diff --git a/lib/core/auth/auth_state_manager.dart b/lib/core/auth/auth_state_manager.dart index 4e451fb..47e609e 100644 --- a/lib/core/auth/auth_state_manager.dart +++ b/lib/core/auth/auth_state_manager.dart @@ -1,4 +1,4 @@ -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; // Types are used through app_providers.dart import '../providers/app_providers.dart'; @@ -7,11 +7,6 @@ import 'token_validator.dart'; import 'auth_cache_manager.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'auth/state'); -} - /// Comprehensive auth state representation @immutable class AuthState { @@ -153,7 +148,7 @@ class AuthStateManager extends Notifier { ); } } catch (e) { - debugPrint('ERROR: Auth initialization failed: $e'); + DebugLogger.error('auth-init-failed', scope: 'auth/state', error: e); state = state.copyWith( status: AuthStatus.error, error: 'Failed to initialize auth: $e', @@ -244,7 +239,7 @@ class AuthStateManager extends Notifier { throw Exception('Invalid API key or insufficient permissions'); } } catch (e) { - debugPrint('ERROR: API key login failed: $e'); + DebugLogger.error('api-key-login-failed', scope: 'auth/state', error: e); state = state.copyWith( status: AuthStatus.error, error: e.toString(), @@ -325,7 +320,7 @@ class AuthStateManager extends Notifier { DebugLogger.auth('Login successful'); return true; } catch (e) { - debugPrint('ERROR: Login failed: $e'); + DebugLogger.error('login-failed', scope: 'auth/state', error: e); state = state.copyWith( status: AuthStatus.error, error: e.toString(), @@ -433,7 +428,7 @@ class AuthStateManager extends Notifier { return await login(username, password, rememberCredentials: false); } } catch (e) { - debugPrint('ERROR: Silent login failed: $e'); + DebugLogger.error('silent-login-failed', scope: 'auth/state', error: e); // Clear invalid credentials on auth errors if (e.toString().contains('401') || @@ -496,7 +491,11 @@ class AuthStateManager extends Notifier { try { await api.logout(); } catch (e) { - debugPrint('Warning: Server logout failed: $e'); + DebugLogger.warning( + 'server-logout-failed', + scope: 'auth/state', + data: {'error': e.toString()}, + ); } } @@ -516,7 +515,7 @@ class AuthStateManager extends Notifier { DebugLogger.auth('Logout complete'); } catch (e) { - debugPrint('ERROR: Logout failed: $e'); + DebugLogger.error('logout-failed', scope: 'auth/state', error: e); // Even if logout fails, clear local state state = state.copyWith( status: AuthStatus.unauthenticated, @@ -551,7 +550,11 @@ class AuthStateManager extends Notifier { // Fall back to server data loading await _loadServerUserData(); } catch (e) { - debugPrint('Warning: Failed to load user data: $e'); + DebugLogger.warning( + 'user-data-load-failed', + scope: 'auth/state', + data: {'error': e.toString()}, + ); // Don't update state on user data load failure } } @@ -563,9 +566,7 @@ class AuthStateManager extends Notifier { if (api != null && state.isAuthenticated) { // Check if we already have user data from token validation if (state.user != null) { - debugPrint( - 'DEBUG: User data already available from token validation', - ); + DebugLogger.auth('user-data-present-from-token', scope: 'auth/state'); return; } @@ -574,7 +575,11 @@ class AuthStateManager extends Notifier { DebugLogger.auth('Loaded complete user data from server'); } } catch (e) { - debugPrint('Warning: Failed to load server user data: $e'); + DebugLogger.warning( + 'server-user-data-load-failed', + scope: 'auth/state', + data: {'error': e.toString()}, + ); // Don't update state on server data load failure - keep JWT data if available } } @@ -605,21 +610,25 @@ class AuthStateManager extends Notifier { // Fast format validation first final formatResult = TokenValidator.validateTokenFormat(token); if (!formatResult.isValid) { - debugPrint('DEBUG: Token format invalid: ${formatResult.message}'); + DebugLogger.warning( + 'token-format-invalid', + scope: 'auth/state', + data: {'message': formatResult.message}, + ); TokenValidationCache.cacheResult(token, formatResult); return false; } // If format is valid but token is expiring soon, try server validation if (formatResult.isExpiringSoon) { - debugPrint('DEBUG: Token expiring soon, validating with server'); + DebugLogger.auth('token-expiring-soon', scope: 'auth/state'); } // Server validation (async with timeout) try { final api = ref.read(apiServiceProvider); if (api == null) { - debugPrint('DEBUG: No API service available for token validation'); + DebugLogger.warning('token-validation-no-api', scope: 'auth/state'); return formatResult.isValid; // Fall back to format validation } @@ -650,7 +659,11 @@ class AuthStateManager extends Notifier { ); return serverResult.isValid; } catch (e) { - debugPrint('DEBUG: Token server validation failed: $e'); + DebugLogger.warning( + 'token-validation-failed', + scope: 'auth/state', + data: {'error': e.toString()}, + ); // On network error, fall back to format validation if it was valid return formatResult.isValid; } diff --git a/lib/core/auth/token_validator.dart b/lib/core/auth/token_validator.dart index c0935ae..84e4355 100644 --- a/lib/core/auth/token_validator.dart +++ b/lib/core/auth/token_validator.dart @@ -2,11 +2,6 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'auth/token-validator'); -} - /// JWT token validation utilities class TokenValidator { static const Duration _validationTimeout = Duration(seconds: 5); @@ -68,8 +63,10 @@ class TokenValidator { ); } catch (e) { // If we can't decode JWT, treat as opaque token - debugPrint( - 'DEBUG: Could not decode JWT payload, treating as opaque token: $e', + DebugLogger.warning( + 'jwt-decode-failed', + scope: 'auth/token-validator', + data: {'error': e.toString()}, ); return TokenValidationResult.valid('Opaque token format valid'); } @@ -153,7 +150,11 @@ class TokenValidator { 'iat': payload['iat'], // Issued at }; } catch (e) { - debugPrint('DEBUG: Could not extract user info from token: $e'); + DebugLogger.warning( + 'token-user-info-failed', + scope: 'auth/token-validator', + data: {'error': e.toString()}, + ); return null; } } diff --git a/lib/core/error/api_error_handler.dart b/lib/core/error/api_error_handler.dart index 9cf655a..72ca59a 100644 --- a/lib/core/error/api_error_handler.dart +++ b/lib/core/error/api_error_handler.dart @@ -1,14 +1,9 @@ import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'api_error.dart'; import 'error_parser.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'api/error-handler'); -} - /// Comprehensive API error handler with structured error parsing /// Handles all types of API errors and converts them to standardized format class ApiErrorHandler { @@ -39,7 +34,10 @@ class ApiErrorHandler { } } catch (e) { // Fallback error if transformation itself fails - debugPrint('ApiErrorHandler: Error transforming exception: $e'); + DebugLogger.log( + 'ApiErrorHandler: Error transforming exception: $e', + scope: 'api/error-handler', + ); return ApiError.unknown( message: 'A system error occurred', originalError: error, @@ -318,21 +316,33 @@ class ApiErrorHandler { String httpMethod, ) { if (kDebugMode) { - debugPrint('🔴 API Error Details:'); - debugPrint(' Method: ${httpMethod.toUpperCase()}'); - debugPrint(' Endpoint: $requestPath'); - debugPrint(' Type: ${dioError.type}'); - debugPrint(' Status: ${dioError.response?.statusCode}'); + DebugLogger.log('🔴 API Error Details:', scope: 'api/error-handler'); + DebugLogger.log( + ' Method: ${httpMethod.toUpperCase()}', + scope: 'api/error-handler', + ); + DebugLogger.log(' Endpoint: $requestPath', scope: 'api/error-handler'); + DebugLogger.log(' Type: ${dioError.type}', scope: 'api/error-handler'); + DebugLogger.log( + ' Status: ${dioError.response?.statusCode}', + scope: 'api/error-handler', + ); if (dioError.response?.data != null) { DebugLogger.error('Response data available (truncated for security)'); } if (dioError.requestOptions.data != null) { - debugPrint(' Request Data: ${dioError.requestOptions.data}'); + DebugLogger.log( + ' Request Data: ${dioError.requestOptions.data}', + scope: 'api/error-handler', + ); } - debugPrint(' Error: ${dioError.message}'); + DebugLogger.log( + ' Error: ${dioError.message}', + scope: 'api/error-handler', + ); } // In production, you would send this to your error tracking service diff --git a/lib/core/error/api_error_interceptor.dart b/lib/core/error/api_error_interceptor.dart index 6cfdb14..6942a2a 100644 --- a/lib/core/error/api_error_interceptor.dart +++ b/lib/core/error/api_error_interceptor.dart @@ -1,14 +1,9 @@ import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'api_error_handler.dart'; import 'api_error.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'api/error-interceptor'); -} - /// Dio interceptor for automatic error handling and transformation /// Converts all HTTP errors into standardized ApiError format class ApiErrorInterceptor extends Interceptor { @@ -51,7 +46,11 @@ class ApiErrorInterceptor extends Interceptor { } } catch (e) { // Fallback if error transformation fails - debugPrint('ApiErrorInterceptor: Failed to transform error: $e'); + DebugLogger.error( + 'transform-failed', + scope: 'api/error-interceptor', + error: e, + ); handler.next(err); } } @@ -71,7 +70,16 @@ class ApiErrorInterceptor extends Interceptor { ); if (logErrors) { - debugPrint('🟡 API Error in successful response: $apiError'); + DebugLogger.warning( + 'successful-response-error', + scope: 'api/error-interceptor', + data: { + 'endpoint': apiError.endpoint, + 'method': apiError.method, + 'status': apiError.statusCode, + 'message': apiError.message, + }, + ); } // Store the error for later handling @@ -122,74 +130,59 @@ class ApiErrorInterceptor extends Interceptor { void _logApiError(ApiError apiError, DioException originalError) { if (!kDebugMode) return; - final typeIcon = _getErrorTypeIcon(apiError.type); - debugPrint('$typeIcon API Error [${apiError.type.name.toUpperCase()}]'); - debugPrint(' Method: ${apiError.method?.toUpperCase() ?? 'UNKNOWN'}'); - debugPrint(' Endpoint: ${apiError.endpoint ?? 'unknown'}'); - debugPrint(' Status: ${apiError.statusCode ?? 'N/A'}'); - debugPrint(' Message: ${apiError.message}'); + final payload = { + 'type': apiError.type.name, + 'endpoint': apiError.endpoint, + 'method': apiError.method, + 'status': apiError.statusCode, + 'message': apiError.message, + if (apiError.technical != null) 'technical': apiError.technical, + if (apiError.retryAfter != null) + 'retryAfterSeconds': apiError.retryAfter!.inSeconds, + 'originalType': originalError.type.name, + }; if (apiError.hasFieldErrors) { - debugPrint(' Field Errors:'); - for (final entry in apiError.fieldErrors.entries) { - final field = entry.key; - final errors = entry.value; - debugPrint(' $field: ${errors.join(', ')}'); - } + payload['fieldErrors'] = { + for (final entry in apiError.fieldErrors.entries) + entry.key: entry.value, + }; } - if (apiError.technical != null) { - debugPrint(' Technical: ${apiError.technical}'); - } - - if (apiError.retryAfter != null) { - debugPrint(' Retry After: ${apiError.retryAfter!.inSeconds}s'); - } - - // Log original error type for debugging - debugPrint(' Original Type: ${originalError.type}'); - - // Log request details if available final requestData = originalError.requestOptions.data; if (requestData != null && requestData.toString().length < 500) { - debugPrint(' Request: $requestData'); + payload['request'] = requestData; } - // Log response data if available and not too large final responseData = originalError.response?.data; if (responseData != null && responseData.toString().length < 1000) { - DebugLogger.error('Response data available (truncated for security)'); + payload['response'] = responseData; } - } - /// Get emoji icon for error type - String _getErrorTypeIcon(ApiErrorType type) { - switch (type) { - case ApiErrorType.network: - return '🌐'; - case ApiErrorType.timeout: - return '⏱️'; - case ApiErrorType.authentication: - return '🔐'; - case ApiErrorType.authorization: - return '🚫'; - case ApiErrorType.validation: - return '✏️'; - case ApiErrorType.badRequest: - return '❌'; - case ApiErrorType.notFound: - return '🔍'; - case ApiErrorType.server: - return '🔥'; - case ApiErrorType.rateLimit: - return '🐌'; - case ApiErrorType.cancelled: - return '🛑'; - case ApiErrorType.security: - return '🔒'; - case ApiErrorType.unknown: - return '❓'; + final headers = originalError.requestOptions.headers; + if (headers.isNotEmpty) { + payload['requestHeaders'] = { + for (final entry in headers.entries) entry.key: entry.value.toString(), + }; } + + final responseHeaders = originalError.response?.headers; + if (responseHeaders != null && responseHeaders.map.isNotEmpty) { + payload['responseHeaders'] = responseHeaders.map; + } + + final requestDuration = + originalError.response?.requestOptions.extra['requestDuration']; + if (requestDuration is Duration) { + payload['requestDurationMs'] = requestDuration.inMilliseconds; + } + + DebugLogger.error( + 'api-error', + scope: 'api/error-interceptor', + data: payload, + error: apiError, + ); } /// Extract ApiError from DioException if available diff --git a/lib/core/error/enhanced_error_service.dart b/lib/core/error/enhanced_error_service.dart index 2da06a7..38bde49 100644 --- a/lib/core/error/enhanced_error_service.dart +++ b/lib/core/error/enhanced_error_service.dart @@ -1,5 +1,5 @@ import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'api_error.dart'; import 'api_error_handler.dart'; @@ -9,11 +9,6 @@ import '../../shared/theme/theme_extensions.dart'; import 'package:conduit/l10n/app_localizations.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'api/error-service'); -} - /// Enhanced error service with comprehensive error handling capabilities /// Provides unified error management across the application class EnhancedErrorService { @@ -306,16 +301,31 @@ class EnhancedErrorService { }) { if (kDebugMode) { final timestamp = DateTime.now().toIso8601String(); - debugPrint('🔴 ERROR [$timestamp] ${context ?? 'Unknown Context'}'); - debugPrint(' Message: ${getUserMessage(error)}'); - debugPrint(' Technical: ${getTechnicalDetails(error)}'); + DebugLogger.log( + '🔴 ERROR [$timestamp] ${context ?? 'Unknown Context'}', + scope: 'api/error-service', + ); + DebugLogger.log( + ' Message: ${getUserMessage(error)}', + scope: 'api/error-service', + ); + DebugLogger.log( + ' Technical: ${getTechnicalDetails(error)}', + scope: 'api/error-service', + ); if (additionalData != null && additionalData.isNotEmpty) { - debugPrint(' Additional Data: $additionalData'); + DebugLogger.log( + ' Additional Data: $additionalData', + scope: 'api/error-service', + ); } if (stackTrace != null) { - debugPrint(' Stack Trace: $stackTrace'); + DebugLogger.log( + ' Stack Trace: $stackTrace', + scope: 'api/error-service', + ); } } diff --git a/lib/core/error/error_parser.dart b/lib/core/error/error_parser.dart index 65b36c7..82fbc93 100644 --- a/lib/core/error/error_parser.dart +++ b/lib/core/error/error_parser.dart @@ -1,11 +1,6 @@ import 'api_error.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'api/error-parser'); -} - /// Comprehensive error response parser /// Handles various API error response formats and extracts structured information class ErrorParser { @@ -29,7 +24,10 @@ class ErrorParser { ); } } catch (e) { - debugPrint('ErrorParser: Error parsing response: $e'); + DebugLogger.log( + 'ErrorParser: Error parsing response: $e', + scope: 'api/error-parser', + ); return ParsedErrorResponse( message: 'Failed to parse error response', metadata: { diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 724d3c9..552bc33 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'package:dio/dio.dart'; // import 'package:http_parser/http_parser.dart'; // Removed legacy websocket/socket.io imports @@ -17,15 +17,15 @@ import '../error/api_error_interceptor.dart'; import 'persistent_streaming_service.dart'; import '../utils/debug_logger.dart'; -const bool _enableLegacyApiLogs = false; +const bool _traceApiLogs = false; const bool _traceConversationParsing = false; const bool _traceFullChatParsing = false; -void debugPrint(String? message, {int? wrapWidth}) { - if (!_enableLegacyApiLogs || message == null) { +void _traceApi(String message) { + if (!_traceApiLogs) { return; } - DebugLogger.fromLegacy(message, scope: 'api/legacy'); + DebugLogger.log(message, scope: 'api/trace'); } class ApiService { @@ -309,15 +309,15 @@ class ApiService { // Safety break to avoid infinite loops (adjust as needed) if (currentPage > 100) { - debugPrint( + _traceApi( 'WARNING: Reached maximum page limit (100), stopping pagination', ); break; } } - debugPrint( - 'DEBUG: Fetched total of ${allRegularChats.length} conversations across $currentPage pages', + _traceApi( + 'Fetched total of ${allRegularChats.length} conversations across $currentPage pages', ); } else { // Original single page fetch @@ -1257,8 +1257,8 @@ class ApiService { String? model, String? systemPrompt, }) async { - debugPrint('DEBUG: Creating new conversation on OpenWebUI server'); - debugPrint('DEBUG: Title: $title, Messages: ${messages.length}'); + _traceApi('Creating new conversation on OpenWebUI server'); + _traceApi('Title: $title, Messages: ${messages.length}'); // Build messages with parent-child relationships final Map messagesMap = {}; @@ -1320,8 +1320,8 @@ class ApiService { 'folder_id': null, }; - debugPrint('DEBUG: Sending chat data with proper parent-child structure'); - debugPrint('DEBUG: Request data: $chatData'); + _traceApi('Sending chat data with proper parent-child structure'); + _traceApi('Request data: $chatData'); final response = await _dio.post('/api/v1/chats/new', data: chatData); @@ -1345,8 +1345,8 @@ class ApiService { String? model, String? systemPrompt, }) async { - debugPrint( - 'DEBUG: Syncing conversation $conversationId with ${messages.length} messages', + _traceApi( + 'Syncing conversation $conversationId with ${messages.length} messages', ); // Build messages map and array in OpenWebUI format @@ -1425,7 +1425,7 @@ class ApiService { }, }; - debugPrint('DEBUG: Syncing chat with OpenWebUI format data using POST'); + _traceApi('Syncing chat with OpenWebUI format data using POST'); // OpenWebUI uses POST not PUT for updating chats await _dio.post('/api/v1/chats/$conversationId', data: chatData); @@ -1452,21 +1452,19 @@ class ApiService { // Pin/Unpin conversation Future pinConversation(String id, bool pinned) async { - debugPrint('DEBUG: ${pinned ? 'Pinning' : 'Unpinning'} conversation: $id'); + _traceApi('${pinned ? 'Pinning' : 'Unpinning'} conversation: $id'); await _dio.post('/api/v1/chats/$id/pin', data: {'pinned': pinned}); } // Archive/Unarchive conversation Future archiveConversation(String id, bool archived) async { - debugPrint( - 'DEBUG: ${archived ? 'Archiving' : 'Unarchiving'} conversation: $id', - ); + _traceApi('${archived ? 'Archiving' : 'Unarchiving'} conversation: $id'); await _dio.post('/api/v1/chats/$id/archive', data: {'archived': archived}); } // Share conversation Future shareConversation(String id) async { - debugPrint('DEBUG: Sharing conversation: $id'); + _traceApi('Sharing conversation: $id'); final response = await _dio.post('/api/v1/chats/$id/share'); final data = response.data as Map; return data['share_id'] as String?; @@ -1474,27 +1472,27 @@ class ApiService { // Clone conversation Future cloneConversation(String id) async { - debugPrint('DEBUG: Cloning conversation: $id'); + _traceApi('Cloning conversation: $id'); final response = await _dio.post('/api/v1/chats/$id/clone'); return _parseFullOpenWebUIChat(response.data as Map); } // User Settings Future> getUserSettings() async { - debugPrint('DEBUG: Fetching user settings'); + _traceApi('Fetching user settings'); final response = await _dio.get('/api/v1/users/user/settings'); return response.data as Map; } Future updateUserSettings(Map settings) async { - debugPrint('DEBUG: Updating user settings'); + _traceApi('Updating user settings'); // Align with web client update route await _dio.post('/api/v1/users/user/settings/update', data: settings); } // Suggestions Future> getSuggestions() async { - debugPrint('DEBUG: Fetching conversation suggestions'); + _traceApi('Fetching conversation suggestions'); final response = await _dio.get('/api/v1/configs/suggestions'); final data = response.data; if (data is List) { @@ -1505,7 +1503,7 @@ class ApiService { // Tools - Check available tools on server Future>> getAvailableTools() async { - debugPrint('DEBUG: Fetching available tools'); + _traceApi('Fetching available tools'); try { final response = await _dio.get('/api/v1/tools/'); final data = response.data; @@ -1513,7 +1511,7 @@ class ApiService { return data.cast>(); } } catch (e) { - debugPrint('DEBUG: Error fetching tools: $e'); + _traceApi('Error fetching tools: $e'); } return []; } @@ -1531,7 +1529,7 @@ class ApiService { final data = response.data; if (data is List) { - debugPrint('DEBUG: Found ${data.length} folders'); + _traceApi('Found ${data.length} folders'); return data.cast>(); } else { DebugLogger.warning( @@ -1551,7 +1549,7 @@ class ApiService { required String name, String? parentId, }) async { - debugPrint('DEBUG: Creating folder: $name'); + _traceApi('Creating folder: $name'); final response = await _dio.post( '/api/v1/folders/', data: {'name': name, if (parentId != null) 'parent_id': parentId}, @@ -1560,7 +1558,7 @@ class ApiService { } Future updateFolder(String id, {String? name, String? parentId}) async { - debugPrint('DEBUG: Updating folder: $id'); + _traceApi('Updating folder: $id'); // OpenWebUI folder update endpoints: // - POST /api/v1/folders/{id}/update -> rename (FolderForm) // - POST /api/v1/folders/{id}/update/parent -> move parent (FolderParentIdForm) @@ -1577,7 +1575,7 @@ class ApiService { } Future deleteFolder(String id) async { - debugPrint('DEBUG: Deleting folder: $id'); + _traceApi('Deleting folder: $id'); await _dio.delete('/api/v1/folders/$id'); } @@ -1585,9 +1583,7 @@ class ApiService { String conversationId, String? folderId, ) async { - debugPrint( - 'DEBUG: Moving conversation $conversationId to folder $folderId', - ); + _traceApi('Moving conversation $conversationId to folder $folderId'); await _dio.post( '/api/v1/chats/$conversationId/folder', data: {'folder_id': folderId}, @@ -1595,7 +1591,7 @@ class ApiService { } Future> getConversationsInFolder(String folderId) async { - debugPrint('DEBUG: Fetching conversations in folder: $folderId'); + _traceApi('Fetching conversations in folder: $folderId'); final response = await _dio.get('/api/v1/chats/folder/$folderId'); final data = response.data; if (data is List) { @@ -1606,7 +1602,7 @@ class ApiService { // Tags Future> getConversationTags(String conversationId) async { - debugPrint('DEBUG: Fetching tags for conversation: $conversationId'); + _traceApi('Fetching tags for conversation: $conversationId'); final response = await _dio.get('/api/v1/chats/$conversationId/tags'); final data = response.data; if (data is List) { @@ -1616,7 +1612,7 @@ class ApiService { } Future addTagToConversation(String conversationId, String tag) async { - debugPrint('DEBUG: Adding tag "$tag" to conversation: $conversationId'); + _traceApi('Adding tag "$tag" to conversation: $conversationId'); await _dio.post('/api/v1/chats/$conversationId/tags', data: {'tag': tag}); } @@ -1624,12 +1620,12 @@ class ApiService { String conversationId, String tag, ) async { - debugPrint('DEBUG: Removing tag "$tag" from conversation: $conversationId'); + _traceApi('Removing tag "$tag" from conversation: $conversationId'); await _dio.delete('/api/v1/chats/$conversationId/tags/$tag'); } Future> getAllTags() async { - debugPrint('DEBUG: Fetching all available tags'); + _traceApi('Fetching all available tags'); final response = await _dio.get('/api/v1/chats/tags'); final data = response.data; if (data is List) { @@ -1639,7 +1635,7 @@ class ApiService { } Future> getConversationsByTag(String tag) async { - debugPrint('DEBUG: Fetching conversations with tag: $tag'); + _traceApi('Fetching conversations with tag: $tag'); final response = await _dio.get('/api/v1/chats/tags/$tag'); final data = response.data; if (data is List) { @@ -1650,7 +1646,7 @@ class ApiService { // Files Future getFileContent(String fileId) async { - debugPrint('DEBUG: Fetching file content: $fileId'); + _traceApi('Fetching file content: $fileId'); // The Open-WebUI endpoint returns the raw file bytes with appropriate // Content-Type headers, not JSON. We must read bytes and base64-encode // them for consistent handling across platforms/widgets. @@ -1683,13 +1679,13 @@ class ApiService { } Future> getFileInfo(String fileId) async { - debugPrint('DEBUG: Fetching file info: $fileId'); + _traceApi('Fetching file info: $fileId'); final response = await _dio.get('/api/v1/files/$fileId'); return response.data as Map; } Future>> getUserFiles() async { - debugPrint('DEBUG: Fetching user files'); + _traceApi('Fetching user files'); final response = await _dio.get('/api/v1/files/'); final data = response.data; if (data is List) { @@ -1705,7 +1701,7 @@ class ApiService { int? limit, int? offset, }) async { - debugPrint('DEBUG: Searching files with query: $query'); + _traceApi('Searching files with query: $query'); final queryParams = {}; if (query != null) queryParams['q'] = query; if (contentType != null) queryParams['content_type'] = contentType; @@ -1724,7 +1720,7 @@ class ApiService { } Future>> getAllFiles() async { - debugPrint('DEBUG: Fetching all files (admin)'); + _traceApi('Fetching all files (admin)'); final response = await _dio.get('/api/v1/files/all'); final data = response.data; if (data is List) { @@ -1738,7 +1734,7 @@ class ApiService { String fileName, { Function(int sent, int total)? onProgress, }) async { - debugPrint('DEBUG: Uploading file with progress: $fileName'); + _traceApi('Uploading file with progress: $fileName'); final formData = FormData.fromMap({ 'file': await MultipartFile.fromFile(filePath, filename: fileName), @@ -1757,7 +1753,7 @@ class ApiService { String fileId, String content, ) async { - debugPrint('DEBUG: Updating file content: $fileId'); + _traceApi('Updating file content: $fileId'); final response = await _dio.post( '/api/v1/files/$fileId/data/content/update', data: {'content': content}, @@ -1766,13 +1762,13 @@ class ApiService { } Future getFileHtmlContent(String fileId) async { - debugPrint('DEBUG: Fetching file HTML content: $fileId'); + _traceApi('Fetching file HTML content: $fileId'); final response = await _dio.get('/api/v1/files/$fileId/content/html'); return response.data as String; } Future deleteFile(String fileId) async { - debugPrint('DEBUG: Deleting file: $fileId'); + _traceApi('Deleting file: $fileId'); await _dio.delete('/api/v1/files/$fileId'); } @@ -1781,7 +1777,7 @@ class ApiService { String? filename, Map? metadata, }) async { - debugPrint('DEBUG: Updating file metadata: $fileId'); + _traceApi('Updating file metadata: $fileId'); final response = await _dio.put( '/api/v1/files/$fileId/metadata', data: { @@ -1797,7 +1793,7 @@ class ApiService { String? operation, Map? options, }) async { - debugPrint('DEBUG: Processing files batch: ${fileIds.length} files'); + _traceApi('Processing files batch: ${fileIds.length} files'); final response = await _dio.post( '/api/v1/retrieval/process/files/batch', data: { @@ -1814,7 +1810,7 @@ class ApiService { } Future>> getFilesByType(String contentType) async { - debugPrint('DEBUG: Fetching files by type: $contentType'); + _traceApi('Fetching files by type: $contentType'); final response = await _dio.get( '/api/v1/files/', queryParameters: {'content_type': contentType}, @@ -1827,14 +1823,14 @@ class ApiService { } Future> getFileStats() async { - debugPrint('DEBUG: Fetching file statistics'); + _traceApi('Fetching file statistics'); final response = await _dio.get('/api/v1/files/stats'); return response.data as Map; } // Knowledge Base Future>> getKnowledgeBases() async { - debugPrint('DEBUG: Fetching knowledge bases'); + _traceApi('Fetching knowledge bases'); final response = await _dio.get('/api/v1/knowledge/'); final data = response.data; if (data is List) { @@ -1847,7 +1843,7 @@ class ApiService { required String name, String? description, }) async { - debugPrint('DEBUG: Creating knowledge base: $name'); + _traceApi('Creating knowledge base: $name'); final response = await _dio.post( '/api/v1/knowledge/', data: {'name': name, if (description != null) 'description': description}, @@ -1860,7 +1856,7 @@ class ApiService { String? name, String? description, }) async { - debugPrint('DEBUG: Updating knowledge base: $id'); + _traceApi('Updating knowledge base: $id'); await _dio.put( '/api/v1/knowledge/$id', data: { @@ -1871,14 +1867,14 @@ class ApiService { } Future deleteKnowledgeBase(String id) async { - debugPrint('DEBUG: Deleting knowledge base: $id'); + _traceApi('Deleting knowledge base: $id'); await _dio.delete('/api/v1/knowledge/$id'); } Future>> getKnowledgeBaseItems( String knowledgeBaseId, ) async { - debugPrint('DEBUG: Fetching knowledge base items: $knowledgeBaseId'); + _traceApi('Fetching knowledge base items: $knowledgeBaseId'); final response = await _dio.get('/api/v1/knowledge/$knowledgeBaseId/items'); final data = response.data; if (data is List) { @@ -1893,7 +1889,7 @@ class ApiService { String? title, Map? metadata, }) async { - debugPrint('DEBUG: Adding item to knowledge base: $knowledgeBaseId'); + _traceApi('Adding item to knowledge base: $knowledgeBaseId'); final response = await _dio.post( '/api/v1/knowledge/$knowledgeBaseId/items', data: { @@ -1909,7 +1905,7 @@ class ApiService { String knowledgeBaseId, String query, ) async { - debugPrint('DEBUG: Searching knowledge base: $knowledgeBaseId for: $query'); + _traceApi('Searching knowledge base: $knowledgeBaseId for: $query'); final response = await _dio.post( '/api/v1/knowledge/$knowledgeBaseId/search', data: {'query': query}, @@ -1923,7 +1919,7 @@ class ApiService { // Web Search Future> performWebSearch(List queries) async { - debugPrint('DEBUG: Performing web search for queries: $queries'); + _traceApi('Performing web search for queries: $queries'); try { final response = await _dio.post( '/api/v1/retrieval/process/web/search', @@ -1944,10 +1940,10 @@ class ApiService { return response.data as Map; } catch (e) { - debugPrint('DEBUG: Web search API error: $e'); + _traceApi('Web search API error: $e'); if (e is DioException) { DebugLogger.error('error-response', scope: 'api/web-search', error: e); - debugPrint('DEBUG: Web search error status: ${e.response?.statusCode}'); + _traceApi('Web search error status: ${e.response?.statusCode}'); } rethrow; } @@ -1967,7 +1963,7 @@ class ApiService { return modelData; } } catch (e) { - debugPrint('DEBUG: Failed to get model details for $modelId: $e'); + _traceApi('Failed to get model details for $modelId: $e'); } return null; } @@ -1981,9 +1977,7 @@ class ApiService { Map? modelItem, String? sessionId, }) async { - debugPrint( - 'DEBUG: Sending chat completed notification (optional endpoint)', - ); + _traceApi('Sending chat completed notification (optional endpoint)'); // This endpoint appears to be optional or deprecated in newer OpenWebUI versions // The main chat synchronization happens through /api/v1/chats/{id} updates @@ -2031,11 +2025,11 @@ class ApiService { receiveTimeout: const Duration(seconds: 4), ), ); - debugPrint('DEBUG: Chat completed response: ${response.statusCode}'); + _traceApi('Chat completed response: ${response.statusCode}'); } catch (e) { // This is a non-critical endpoint - main sync happens via /api/v1/chats/{id} - debugPrint( - 'DEBUG: Chat completed endpoint not available or failed (non-critical): $e', + _traceApi( + 'Chat completed endpoint not available or failed (non-critical): $e', ); } } @@ -2045,9 +2039,7 @@ class ApiService { String collectionName, String query, ) async { - debugPrint( - 'DEBUG: Querying collection: $collectionName with query: $query', - ); + _traceApi('Querying collection: $collectionName with query: $query'); try { final response = await _dio.post( '/api/v1/retrieval/query/collection', @@ -2058,12 +2050,8 @@ class ApiService { }, ); - debugPrint( - 'DEBUG: Collection query response status: ${response.statusCode}', - ); - debugPrint( - 'DEBUG: Collection query response type: ${response.data.runtimeType}', - ); + _traceApi('Collection query response status: ${response.statusCode}'); + _traceApi('Collection query response type: ${response.data.runtimeType}'); DebugLogger.log( 'query-ok', scope: 'api/collection', @@ -2086,14 +2074,10 @@ class ApiService { return []; } catch (e) { - debugPrint('DEBUG: Collection query API error: $e'); + _traceApi('Collection query API error: $e'); if (e is DioException) { - debugPrint( - 'DEBUG: Collection query error response: ${e.response?.data}', - ); - debugPrint( - 'DEBUG: Collection query error status: ${e.response?.statusCode}', - ); + _traceApi('Collection query error response: ${e.response?.data}'); + _traceApi('Collection query error status: ${e.response?.statusCode}'); } rethrow; } @@ -2101,25 +2085,19 @@ class ApiService { // Get retrieval configuration to check web search settings Future> getRetrievalConfig() async { - debugPrint('DEBUG: Getting retrieval configuration'); + _traceApi('Getting retrieval configuration'); try { final response = await _dio.get('/api/v1/retrieval/config'); - debugPrint( - 'DEBUG: Retrieval config response status: ${response.statusCode}', - ); + _traceApi('Retrieval config response status: ${response.statusCode}'); DebugLogger.log('config-ok', scope: 'api/retrieval'); return response.data as Map; } catch (e) { - debugPrint('DEBUG: Retrieval config API error: $e'); + _traceApi('Retrieval config API error: $e'); if (e is DioException) { - debugPrint( - 'DEBUG: Retrieval config error response: ${e.response?.data}', - ); - debugPrint( - 'DEBUG: Retrieval config error status: ${e.response?.statusCode}', - ); + _traceApi('Retrieval config error response: ${e.response?.data}'); + _traceApi('Retrieval config error status: ${e.response?.statusCode}'); } rethrow; } @@ -2127,7 +2105,7 @@ class ApiService { // Audio Future> getAvailableVoices() async { - debugPrint('DEBUG: Fetching available voices'); + _traceApi('Fetching available voices'); final response = await _dio.get('/api/v1/audio/voices'); final data = response.data; if (data is List) { @@ -2141,7 +2119,7 @@ class ApiService { String? voice, }) async { final textPreview = text.length > 50 ? text.substring(0, 50) : text; - debugPrint('DEBUG: Generating speech for text: $textPreview...'); + _traceApi('Generating speech for text: $textPreview...'); final response = await _dio.post( '/api/v1/audio/speech', data: {'text': text, if (voice != null) 'voice': voice}, @@ -2158,7 +2136,7 @@ class ApiService { // Image Generation Future>> getImageModels() async { - debugPrint('DEBUG: Fetching image generation models'); + _traceApi('Fetching image generation models'); final response = await _dio.get('/api/v1/images/models'); final data = response.data; if (data is List) { @@ -2176,7 +2154,7 @@ class ApiService { double? guidance, }) async { final promptPreview = prompt.length > 50 ? prompt.substring(0, 50) : prompt; - debugPrint('DEBUG: Generating image with prompt: $promptPreview...'); + _traceApi('Generating image with prompt: $promptPreview...'); try { final response = await _dio.post( '/api/v1/images/generations', @@ -2191,7 +2169,7 @@ class ApiService { ); return response.data; } on DioException catch (e) { - debugPrint('DEBUG: images/generations failed: ${e.response?.statusCode}'); + _traceApi('images/generations failed: ${e.response?.statusCode}'); DebugLogger.error( 'images-generate-failed', scope: 'api/images', @@ -2205,7 +2183,7 @@ class ApiService { // Prompts Future>> getPrompts() async { - debugPrint('DEBUG: Fetching prompts'); + _traceApi('Fetching prompts'); final response = await _dio.get('/api/v1/prompts/'); final data = response.data; if (data is List) { @@ -2216,17 +2194,15 @@ class ApiService { // Permissions & Features Future> getUserPermissions() async { - debugPrint('DEBUG: Fetching user permissions'); + _traceApi('Fetching user permissions'); try { final response = await _dio.get('/api/v1/users/permissions'); return response.data as Map; } catch (e) { - debugPrint('DEBUG: Error fetching user permissions: $e'); + _traceApi('Error fetching user permissions: $e'); if (e is DioException) { - debugPrint('DEBUG: Permissions error response: ${e.response?.data}'); - debugPrint( - 'DEBUG: Permissions error status: ${e.response?.statusCode}', - ); + _traceApi('Permissions error response: ${e.response?.data}'); + _traceApi('Permissions error status: ${e.response?.statusCode}'); } rethrow; } @@ -2238,7 +2214,7 @@ class ApiService { String? description, List? tags, }) async { - debugPrint('DEBUG: Creating prompt: $title'); + _traceApi('Creating prompt: $title'); final response = await _dio.post( '/api/v1/prompts/', data: { @@ -2258,7 +2234,7 @@ class ApiService { String? description, List? tags, }) async { - debugPrint('DEBUG: Updating prompt: $id'); + _traceApi('Updating prompt: $id'); await _dio.put( '/api/v1/prompts/$id', data: { @@ -2271,13 +2247,13 @@ class ApiService { } Future deletePrompt(String id) async { - debugPrint('DEBUG: Deleting prompt: $id'); + _traceApi('Deleting prompt: $id'); await _dio.delete('/api/v1/prompts/$id'); } // Tools & Functions Future>> getTools() async { - debugPrint('DEBUG: Fetching tools'); + _traceApi('Fetching tools'); final response = await _dio.get('/api/v1/tools/'); final data = response.data; if (data is List) { @@ -2287,7 +2263,7 @@ class ApiService { } Future>> getFunctions() async { - debugPrint('DEBUG: Fetching functions'); + _traceApi('Fetching functions'); final response = await _dio.get('/api/v1/functions/'); final data = response.data; if (data is List) { @@ -2300,7 +2276,7 @@ class ApiService { required String name, required Map spec, }) async { - debugPrint('DEBUG: Creating tool: $name'); + _traceApi('Creating tool: $name'); final response = await _dio.post( '/api/v1/tools/', data: {'name': name, 'spec': spec}, @@ -2313,7 +2289,7 @@ class ApiService { required String code, String? description, }) async { - debugPrint('DEBUG: Creating function: $name'); + _traceApi('Creating function: $name'); final response = await _dio.post( '/api/v1/functions/', data: { @@ -2327,7 +2303,7 @@ class ApiService { // Enhanced Tools Management Operations Future> getTool(String toolId) async { - debugPrint('DEBUG: Fetching tool details: $toolId'); + _traceApi('Fetching tool details: $toolId'); final response = await _dio.get('/api/v1/tools/id/$toolId'); return response.data as Map; } @@ -2338,7 +2314,7 @@ class ApiService { Map? spec, String? description, }) async { - debugPrint('DEBUG: Updating tool: $toolId'); + _traceApi('Updating tool: $toolId'); final response = await _dio.post( '/api/v1/tools/id/$toolId/update', data: { @@ -2351,12 +2327,12 @@ class ApiService { } Future deleteTool(String toolId) async { - debugPrint('DEBUG: Deleting tool: $toolId'); + _traceApi('Deleting tool: $toolId'); await _dio.delete('/api/v1/tools/id/$toolId/delete'); } Future> getToolValves(String toolId) async { - debugPrint('DEBUG: Fetching tool valves: $toolId'); + _traceApi('Fetching tool valves: $toolId'); final response = await _dio.get('/api/v1/tools/id/$toolId/valves'); return response.data as Map; } @@ -2365,7 +2341,7 @@ class ApiService { String toolId, Map valves, ) async { - debugPrint('DEBUG: Updating tool valves: $toolId'); + _traceApi('Updating tool valves: $toolId'); final response = await _dio.post( '/api/v1/tools/id/$toolId/valves/update', data: valves, @@ -2374,7 +2350,7 @@ class ApiService { } Future> getUserToolValves(String toolId) async { - debugPrint('DEBUG: Fetching user tool valves: $toolId'); + _traceApi('Fetching user tool valves: $toolId'); final response = await _dio.get('/api/v1/tools/id/$toolId/valves/user'); return response.data as Map; } @@ -2383,7 +2359,7 @@ class ApiService { String toolId, Map valves, ) async { - debugPrint('DEBUG: Updating user tool valves: $toolId'); + _traceApi('Updating user tool valves: $toolId'); final response = await _dio.post( '/api/v1/tools/id/$toolId/valves/user/update', data: valves, @@ -2392,7 +2368,7 @@ class ApiService { } Future>> exportTools() async { - debugPrint('DEBUG: Exporting tools configuration'); + _traceApi('Exporting tools configuration'); final response = await _dio.get('/api/v1/tools/export'); final data = response.data; if (data is List) { @@ -2402,7 +2378,7 @@ class ApiService { } Future> loadToolFromUrl(String url) async { - debugPrint('DEBUG: Loading tool from URL: $url'); + _traceApi('Loading tool from URL: $url'); final response = await _dio.post( '/api/v1/tools/load/url', data: {'url': url}, @@ -2412,7 +2388,7 @@ class ApiService { // Enhanced Functions Management Operations Future> getFunction(String functionId) async { - debugPrint('DEBUG: Fetching function details: $functionId'); + _traceApi('Fetching function details: $functionId'); final response = await _dio.get('/api/v1/functions/id/$functionId'); return response.data as Map; } @@ -2423,7 +2399,7 @@ class ApiService { String? code, String? description, }) async { - debugPrint('DEBUG: Updating function: $functionId'); + _traceApi('Updating function: $functionId'); final response = await _dio.post( '/api/v1/functions/id/$functionId/update', data: { @@ -2436,18 +2412,18 @@ class ApiService { } Future deleteFunction(String functionId) async { - debugPrint('DEBUG: Deleting function: $functionId'); + _traceApi('Deleting function: $functionId'); await _dio.delete('/api/v1/functions/id/$functionId/delete'); } Future> toggleFunction(String functionId) async { - debugPrint('DEBUG: Toggling function: $functionId'); + _traceApi('Toggling function: $functionId'); final response = await _dio.post('/api/v1/functions/id/$functionId/toggle'); return response.data as Map; } Future> toggleGlobalFunction(String functionId) async { - debugPrint('DEBUG: Toggling global function: $functionId'); + _traceApi('Toggling global function: $functionId'); final response = await _dio.post( '/api/v1/functions/id/$functionId/toggle/global', ); @@ -2455,7 +2431,7 @@ class ApiService { } Future> getFunctionValves(String functionId) async { - debugPrint('DEBUG: Fetching function valves: $functionId'); + _traceApi('Fetching function valves: $functionId'); final response = await _dio.get('/api/v1/functions/id/$functionId/valves'); return response.data as Map; } @@ -2464,7 +2440,7 @@ class ApiService { String functionId, Map valves, ) async { - debugPrint('DEBUG: Updating function valves: $functionId'); + _traceApi('Updating function valves: $functionId'); final response = await _dio.post( '/api/v1/functions/id/$functionId/valves/update', data: valves, @@ -2473,7 +2449,7 @@ class ApiService { } Future> getUserFunctionValves(String functionId) async { - debugPrint('DEBUG: Fetching user function valves: $functionId'); + _traceApi('Fetching user function valves: $functionId'); final response = await _dio.get( '/api/v1/functions/id/$functionId/valves/user', ); @@ -2484,7 +2460,7 @@ class ApiService { String functionId, Map valves, ) async { - debugPrint('DEBUG: Updating user function valves: $functionId'); + _traceApi('Updating user function valves: $functionId'); final response = await _dio.post( '/api/v1/functions/id/$functionId/valves/user/update', data: valves, @@ -2493,13 +2469,13 @@ class ApiService { } Future> syncFunctions() async { - debugPrint('DEBUG: Syncing functions'); + _traceApi('Syncing functions'); final response = await _dio.post('/api/v1/functions/sync'); return response.data as Map; } Future>> exportFunctions() async { - debugPrint('DEBUG: Exporting functions configuration'); + _traceApi('Exporting functions configuration'); final response = await _dio.get('/api/v1/functions/export'); final data = response.data; if (data is List) { @@ -2510,7 +2486,7 @@ class ApiService { // Memory & Notes Future>> getMemories() async { - debugPrint('DEBUG: Fetching memories'); + _traceApi('Fetching memories'); final response = await _dio.get('/api/v1/memories/'); final data = response.data; if (data is List) { @@ -2523,7 +2499,7 @@ class ApiService { required String content, String? title, }) async { - debugPrint('DEBUG: Creating memory'); + _traceApi('Creating memory'); final response = await _dio.post( '/api/v1/memories/', data: {'content': content, if (title != null) 'title': title}, @@ -2532,7 +2508,7 @@ class ApiService { } Future>> getNotes() async { - debugPrint('DEBUG: Fetching notes'); + _traceApi('Fetching notes'); final response = await _dio.get('/api/v1/notes/'); final data = response.data; if (data is List) { @@ -2546,7 +2522,7 @@ class ApiService { required String content, List? tags, }) async { - debugPrint('DEBUG: Creating note: $title'); + _traceApi('Creating note: $title'); final response = await _dio.post( '/api/v1/notes/', data: { @@ -2564,7 +2540,7 @@ class ApiService { String? content, List? tags, }) async { - debugPrint('DEBUG: Updating note: $id'); + _traceApi('Updating note: $id'); await _dio.put( '/api/v1/notes/$id', data: { @@ -2576,13 +2552,13 @@ class ApiService { } Future deleteNote(String id) async { - debugPrint('DEBUG: Deleting note: $id'); + _traceApi('Deleting note: $id'); await _dio.delete('/api/v1/notes/$id'); } // Team Collaboration Future>> getChannels() async { - debugPrint('DEBUG: Fetching channels'); + _traceApi('Fetching channels'); final response = await _dio.get('/api/v1/channels/'); final data = response.data; if (data is List) { @@ -2596,7 +2572,7 @@ class ApiService { String? description, bool isPrivate = false, }) async { - debugPrint('DEBUG: Creating channel: $name'); + _traceApi('Creating channel: $name'); final response = await _dio.post( '/api/v1/channels/', data: { @@ -2609,17 +2585,17 @@ class ApiService { } Future joinChannel(String channelId) async { - debugPrint('DEBUG: Joining channel: $channelId'); + _traceApi('Joining channel: $channelId'); await _dio.post('/api/v1/channels/$channelId/join'); } Future leaveChannel(String channelId) async { - debugPrint('DEBUG: Leaving channel: $channelId'); + _traceApi('Leaving channel: $channelId'); await _dio.post('/api/v1/channels/$channelId/leave'); } Future>> getChannelMembers(String channelId) async { - debugPrint('DEBUG: Fetching channel members: $channelId'); + _traceApi('Fetching channel members: $channelId'); final response = await _dio.get('/api/v1/channels/$channelId/members'); final data = response.data; if (data is List) { @@ -2629,7 +2605,7 @@ class ApiService { } Future> getChannelConversations(String channelId) async { - debugPrint('DEBUG: Fetching channel conversations: $channelId'); + _traceApi('Fetching channel conversations: $channelId'); final response = await _dio.get('/api/v1/channels/$channelId/chats'); final data = response.data; if (data is List) { @@ -2640,7 +2616,7 @@ class ApiService { // Enhanced Channel Management Operations Future> getChannel(String channelId) async { - debugPrint('DEBUG: Fetching channel details: $channelId'); + _traceApi('Fetching channel details: $channelId'); final response = await _dio.get('/api/v1/channels/$channelId'); return response.data as Map; } @@ -2651,7 +2627,7 @@ class ApiService { String? description, bool? isPrivate, }) async { - debugPrint('DEBUG: Updating channel: $channelId'); + _traceApi('Updating channel: $channelId'); final response = await _dio.post( '/api/v1/channels/$channelId/update', data: { @@ -2664,7 +2640,7 @@ class ApiService { } Future deleteChannel(String channelId) async { - debugPrint('DEBUG: Deleting channel: $channelId'); + _traceApi('Deleting channel: $channelId'); await _dio.delete('/api/v1/channels/$channelId/delete'); } @@ -2675,7 +2651,7 @@ class ApiService { DateTime? before, DateTime? after, }) async { - debugPrint('DEBUG: Fetching channel messages: $channelId'); + _traceApi('Fetching channel messages: $channelId'); final queryParams = {}; if (limit != null) queryParams['limit'] = limit; if (offset != null) queryParams['offset'] = offset; @@ -2699,7 +2675,7 @@ class ApiService { String? messageType, Map? metadata, }) async { - debugPrint('DEBUG: Posting message to channel: $channelId'); + _traceApi('Posting message to channel: $channelId'); final response = await _dio.post( '/api/v1/channels/$channelId/messages/post', data: { @@ -2717,7 +2693,7 @@ class ApiService { String? content, Map? metadata, }) async { - debugPrint('DEBUG: Updating channel message: $channelId/$messageId'); + _traceApi('Updating channel message: $channelId/$messageId'); final response = await _dio.post( '/api/v1/channels/$channelId/messages/$messageId/update', data: { @@ -2729,7 +2705,7 @@ class ApiService { } Future deleteChannelMessage(String channelId, String messageId) async { - debugPrint('DEBUG: Deleting channel message: $channelId/$messageId'); + _traceApi('Deleting channel message: $channelId/$messageId'); await _dio.delete('/api/v1/channels/$channelId/messages/$messageId'); } @@ -2738,7 +2714,7 @@ class ApiService { String messageId, String emoji, ) async { - debugPrint('DEBUG: Adding reaction to message: $channelId/$messageId'); + _traceApi('Adding reaction to message: $channelId/$messageId'); final response = await _dio.post( '/api/v1/channels/$channelId/messages/$messageId/reactions', data: {'emoji': emoji}, @@ -2751,7 +2727,7 @@ class ApiService { String messageId, String emoji, ) async { - debugPrint('DEBUG: Removing reaction from message: $channelId/$messageId'); + _traceApi('Removing reaction from message: $channelId/$messageId'); await _dio.delete( '/api/v1/channels/$channelId/messages/$messageId/reactions/$emoji', ); @@ -2761,7 +2737,7 @@ class ApiService { String channelId, String messageId, ) async { - debugPrint('DEBUG: Fetching message reactions: $channelId/$messageId'); + _traceApi('Fetching message reactions: $channelId/$messageId'); final response = await _dio.get( '/api/v1/channels/$channelId/messages/$messageId/reactions', ); @@ -2776,7 +2752,7 @@ class ApiService { String channelId, String messageId, ) async { - debugPrint('DEBUG: Fetching message thread: $channelId/$messageId'); + _traceApi('Fetching message thread: $channelId/$messageId'); final response = await _dio.get( '/api/v1/channels/$channelId/messages/$messageId/thread', ); @@ -2793,7 +2769,7 @@ class ApiService { required String content, Map? metadata, }) async { - debugPrint('DEBUG: Replying to message: $channelId/$messageId'); + _traceApi('Replying to message: $channelId/$messageId'); final response = await _dio.post( '/api/v1/channels/$channelId/messages/$messageId/reply', data: {'content': content, if (metadata != null) 'metadata': metadata}, @@ -2802,7 +2778,7 @@ class ApiService { } Future markChannelRead(String channelId, {String? messageId}) async { - debugPrint('DEBUG: Marking channel as read: $channelId'); + _traceApi('Marking channel as read: $channelId'); await _dio.post( '/api/v1/channels/$channelId/read', data: {if (messageId != null) 'last_read_message_id': messageId}, @@ -2810,7 +2786,7 @@ class ApiService { } Future> getChannelUnreadCount(String channelId) async { - debugPrint('DEBUG: Fetching unread count for channel: $channelId'); + _traceApi('Fetching unread count for channel: $channelId'); final response = await _dio.get('/api/v1/channels/$channelId/unread'); return response.data as Map; } @@ -2910,12 +2886,12 @@ class ApiService { // Add feature flags if enabled if (enableWebSearch) { data['web_search'] = true; - debugPrint('DEBUG: Web search enabled in SSE request'); + _traceApi('Web search enabled in SSE request'); } if (enableImageGeneration) { // Mirror web_search behavior for image generation data['image_generation'] = true; - debugPrint('DEBUG: Image generation enabled in SSE request'); + _traceApi('Image generation enabled in SSE request'); } if (enableWebSearch || enableImageGeneration) { @@ -2933,7 +2909,7 @@ class ApiService { // Add tool_ids if provided (Open-WebUI expects tool_ids as array of strings) if (toolIds != null && toolIds.isNotEmpty) { data['tool_ids'] = toolIds; - debugPrint('DEBUG: Including tool_ids in SSE request: $toolIds'); + _traceApi('Including tool_ids in SSE request: $toolIds'); // Hint server to use native function calling when tools are selected // This enables provider-native tool execution paths and consistent UI events @@ -2942,7 +2918,7 @@ class ApiService { (data['params'] as Map?) ?? {}; params['function_calling'] = 'native'; data['params'] = params; - debugPrint('DEBUG: Set params.function_calling = native'); + _traceApi('Set params.function_calling = native'); } catch (_) { // Non-fatal; continue without forcing native mode } @@ -2951,39 +2927,31 @@ class ApiService { // Include tool_servers if provided (for native function calling with OpenAPI servers) if (toolServers != null && toolServers.isNotEmpty) { data['tool_servers'] = toolServers; - debugPrint( - 'DEBUG: Including tool_servers in request (${toolServers.length})', - ); + _traceApi('Including tool_servers in request (${toolServers.length})'); } // Include non-image files at the top level as expected by Open WebUI if (allFiles.isNotEmpty) { data['files'] = allFiles; - debugPrint( - 'DEBUG: Including non-image files in request: ${allFiles.length}', - ); + _traceApi('Including non-image files in request: ${allFiles.length}'); } // Don't add session_id or id - they break SSE streaming! // The server falls back to task-based async when these are present - debugPrint('DEBUG: Starting SSE streaming request'); - debugPrint('DEBUG: Model: $model'); - debugPrint('DEBUG: Message count: ${processedMessages.length}'); + _traceApi('Starting SSE streaming request'); + _traceApi('Model: $model'); + _traceApi('Message count: ${processedMessages.length}'); // Debug the data being sent - debugPrint('DEBUG: SSE request data keys (pre-bg): ${data.keys.toList()}'); - debugPrint( - 'DEBUG: Has background_tasks (pre-bg): ${data.containsKey('background_tasks')}', + _traceApi('SSE request data keys (pre-bg): ${data.keys.toList()}'); + _traceApi( + 'Has background_tasks (pre-bg): ${data.containsKey('background_tasks')}', ); - debugPrint( - 'DEBUG: Has session_id (pre-bg): ${data.containsKey('session_id')}', - ); - debugPrint( - 'DEBUG: background_tasks value (pre-bg): ${data['background_tasks']}', - ); - debugPrint('DEBUG: session_id value (pre-bg): ${data['session_id']}'); - debugPrint('DEBUG: id value (pre-bg): ${data['id']}'); + _traceApi('Has session_id (pre-bg): ${data.containsKey('session_id')}'); + _traceApi('background_tasks value (pre-bg): ${data['background_tasks']}'); + _traceApi('session_id value (pre-bg): ${data['session_id']}'); + _traceApi('id value (pre-bg): ${data['id']}'); // Decide whether to use background task flow. // Use background task mode when tools, web_search, image_generation are enabled, @@ -3010,18 +2978,18 @@ class ApiService { } // Extra diagnostics to confirm dynamic-channel payload - debugPrint('DEBUG: Background flow payload keys: ${data.keys.toList()}'); - debugPrint('DEBUG: Using session_id: $sessionId'); - debugPrint('DEBUG: Using message id: $messageId'); - debugPrint( - 'DEBUG: Has tool_ids: ${data.containsKey('tool_ids')} -> ${data['tool_ids']}', + _traceApi('Background flow payload keys: ${data.keys.toList()}'); + _traceApi('Using session_id: $sessionId'); + _traceApi('Using message id: $messageId'); + _traceApi( + 'Has tool_ids: ${data.containsKey('tool_ids')} -> ${data['tool_ids']}', ); - debugPrint( - 'DEBUG: Has background_tasks: ${data.containsKey('background_tasks')}', + _traceApi( + 'Has background_tasks: ${data.containsKey('background_tasks')}', ); - debugPrint('DEBUG: Initiating background tools flow (task-based)'); - debugPrint('DEBUG: Posting to /api/chat/completions (no SSE)'); + _traceApi('Initiating background tools flow (task-based)'); + _traceApi('Posting to /api/chat/completions (no SSE)'); // Fire in background; poll chat for updates and stream deltas to UI () async { @@ -3031,7 +2999,7 @@ class ApiService { final taskId = (respData is Map) ? (respData['task_id']?.toString()) : null; - debugPrint('DEBUG: Background task created: $taskId'); + _traceApi('Background task created: $taskId'); // If no session/socket provided, fall back to polling for updates. if (sessionIdOverride == null || sessionIdOverride.isEmpty) { @@ -3047,7 +3015,7 @@ class ApiService { } } } catch (e) { - debugPrint('DEBUG: Background tools flow failed: $e'); + _traceApi('Background tools flow failed: $e'); if (!streamController.isClosed) streamController.close(); } }(); @@ -3154,7 +3122,7 @@ class ApiService { cancelOnError: true, ); } catch (e) { - debugPrint('DEBUG: SSE streaming failed: $e'); + _traceApi('SSE streaming failed: $e'); if (!streamController.isClosed) streamController.close(); } }(); @@ -3468,7 +3436,7 @@ class ApiService { // File upload for RAG Future uploadFile(String filePath, String fileName) async { - debugPrint('DEBUG: Starting file upload: $fileName from $filePath'); + _traceApi('Starting file upload: $fileName from $filePath'); try { // Check if file exists @@ -3481,7 +3449,7 @@ class ApiService { 'file': await MultipartFile.fromFile(filePath, filename: fileName), }); - debugPrint('DEBUG: Uploading to /api/v1/files/'); + _traceApi('Uploading to /api/v1/files/'); final response = await _dio.post('/api/v1/files/', data: formData); DebugLogger.log( @@ -3493,7 +3461,7 @@ class ApiService { if (response.data is Map && response.data['id'] != null) { final fileId = response.data['id'] as String; - debugPrint('DEBUG: File uploaded successfully with ID: $fileId'); + _traceApi('File uploaded successfully with ID: $fileId'); return fileId; } else { throw Exception('Invalid response format: missing file ID'); @@ -3516,9 +3484,9 @@ class ApiService { // Debug method to test API endpoints Future debugApiEndpoints() async { - debugPrint('=== DEBUG API ENDPOINTS ==='); - debugPrint('Server URL: ${serverConfig.url}'); - debugPrint('Auth token present: ${authToken != null}'); + _traceApi('=== DEBUG API ENDPOINTS ==='); + _traceApi('Server URL: ${serverConfig.url}'); + _traceApi('Auth token present: ${authToken != null}'); // Test different possible endpoints final endpoints = [ @@ -3530,9 +3498,9 @@ class ApiService { for (final endpoint in endpoints) { try { - debugPrint('Testing endpoint: $endpoint'); + _traceApi('Testing endpoint: $endpoint'); final response = await _dio.get(endpoint); - debugPrint('✅ $endpoint - Status: ${response.statusCode}'); + _traceApi('✅ $endpoint - Status: ${response.statusCode}'); DebugLogger.log( 'response-type', scope: 'api/diagnostics', @@ -3563,33 +3531,33 @@ class ApiService { data: {'endpoint': endpoint, 'preview': response.data.toString()}, ); } catch (e) { - debugPrint('❌ $endpoint - Error: $e'); + _traceApi('❌ $endpoint - Error: $e'); } - debugPrint('---'); + _traceApi('---'); } - debugPrint('=== END DEBUG ==='); + _traceApi('=== END DEBUG ==='); } // Check if server has API documentation Future checkApiDocumentation() async { - debugPrint('=== CHECKING API DOCUMENTATION ==='); + _traceApi('=== CHECKING API DOCUMENTATION ==='); final docEndpoints = ['/docs', '/api/docs', '/swagger', '/api/swagger']; for (final endpoint in docEndpoints) { try { final response = await _dio.get(endpoint); if (response.statusCode == 200) { - debugPrint('✅ API docs available at: ${serverConfig.url}$endpoint'); + _traceApi('✅ API docs available at: ${serverConfig.url}$endpoint'); if (response.data is String && response.data.toString().contains('swagger')) { - debugPrint(' This appears to be Swagger documentation'); + _traceApi(' This appears to be Swagger documentation'); } } } catch (e) { - debugPrint('❌ No docs at $endpoint'); + _traceApi('❌ No docs at $endpoint'); } } - debugPrint('=== END API DOCS CHECK ==='); + _traceApi('=== END API DOCS CHECK ==='); } // dispose() removed – no legacy websocket resources to clean up @@ -3604,7 +3572,7 @@ class ApiService { String? folderId, bool overwriteExisting = false, }) async { - debugPrint('DEBUG: Importing ${chatsData.length} chats'); + _traceApi('Importing ${chatsData.length} chats'); final response = await _dio.post( '/api/v1/chats/import', data: { @@ -3627,8 +3595,8 @@ class ApiService { bool includeMessages = true, String? format, }) async { - debugPrint( - 'DEBUG: Exporting chats${chatIds != null ? ' (${chatIds.length} chats)' : ''}', + _traceApi( + 'Exporting chats${chatIds != null ? ' (${chatIds.length} chats)' : ''}', ); final queryParams = {}; if (chatIds != null) queryParams['chat_ids'] = chatIds.join(','); @@ -3652,7 +3620,7 @@ class ApiService { List? excludeIds, String? beforeDate, }) async { - debugPrint('DEBUG: Archiving all chats in bulk'); + _traceApi('Archiving all chats in bulk'); final response = await _dio.post( '/api/v1/chats/archive/all', data: { @@ -3669,7 +3637,7 @@ class ApiService { String? beforeDate, bool archived = false, }) async { - debugPrint('DEBUG: Deleting all chats in bulk (archived: $archived)'); + _traceApi('Deleting all chats in bulk (archived: $archived)'); final response = await _dio.post( '/api/v1/chats/delete/all', data: { @@ -3683,7 +3651,7 @@ class ApiService { /// Get pinned chats Future> getPinnedChats() async { - debugPrint('DEBUG: Fetching pinned chats'); + _traceApi('Fetching pinned chats'); final response = await _dio.get('/api/v1/chats/pinned'); final data = response.data; if (data is List) { @@ -3694,7 +3662,7 @@ class ApiService { /// Get archived chats Future> getArchivedChats({int? limit, int? offset}) async { - debugPrint('DEBUG: Fetching archived chats'); + _traceApi('Fetching archived chats'); final queryParams = {}; if (limit != null) queryParams['limit'] = limit; if (offset != null) queryParams['offset'] = offset; @@ -3726,7 +3694,7 @@ class ApiService { String? sortBy, String? sortOrder, }) async { - debugPrint('DEBUG: Searching chats with query: $query'); + _traceApi('Searching chats with query: $query'); final queryParams = {}; // OpenAPI expects 'text' for this endpoint; keep extras if server tolerates them if (query != null) queryParams['text'] = query; @@ -3784,7 +3752,7 @@ class ApiService { int? limit, int? offset, }) async { - debugPrint('DEBUG: Searching messages with query: $query'); + _traceApi('Searching messages with query: $query'); // Build query parameters; include both 'text' and 'query' for compatibility final qp = { @@ -3812,8 +3780,8 @@ class ApiService { // If not supported, quietly return empty results if (response.statusCode == 404 || response.statusCode == 405) { - debugPrint( - 'DEBUG: messages search endpoint not supported (status: ${response.statusCode})', + _traceApi( + 'messages search endpoint not supported (status: ${response.statusCode})', ); return []; } @@ -3831,7 +3799,7 @@ class ApiService { return []; } on DioException catch (e) { // On any transport or other error, degrade gracefully without surfacing - debugPrint('DEBUG: messages search request failed gracefully: ${e.type}'); + _traceApi('messages search request failed gracefully: ${e.type}'); return []; } } @@ -3842,7 +3810,7 @@ class ApiService { DateTime? fromDate, DateTime? toDate, }) async { - debugPrint('DEBUG: Fetching chat statistics'); + _traceApi('Fetching chat statistics'); final queryParams = {}; if (userId != null) queryParams['user_id'] = userId; if (fromDate != null) queryParams['from_date'] = fromDate.toIso8601String(); @@ -3857,7 +3825,7 @@ class ApiService { /// Duplicate/copy a chat Future duplicateChat(String chatId, {String? title}) async { - debugPrint('DEBUG: Duplicating chat: $chatId'); + _traceApi('Duplicating chat: $chatId'); final response = await _dio.post( '/api/v1/chats/$chatId/duplicate', data: {if (title != null) 'title': title}, @@ -3867,7 +3835,7 @@ class ApiService { /// Get recent chats with activity Future> getRecentChats({int limit = 10, int? days}) async { - debugPrint('DEBUG: Fetching recent chats (limit: $limit)'); + _traceApi('Fetching recent chats (limit: $limit)'); final queryParams = {'limit': limit}; if (days != null) queryParams['days'] = days; @@ -3894,7 +3862,7 @@ class ApiService { String? sortBy, String? sortOrder, }) async { - debugPrint('DEBUG: Fetching chat history with filters'); + _traceApi('Fetching chat history with filters'); final queryParams = {}; if (limit != null) queryParams['limit'] = limit; if (offset != null) queryParams['offset'] = offset; @@ -3920,8 +3888,8 @@ class ApiService { operation, // 'archive', 'delete', 'pin', 'unpin', 'move_to_folder' Map? params, }) async { - debugPrint( - 'DEBUG: Performing batch operation "$operation" on ${chatIds.length} chats', + _traceApi( + 'Performing batch operation "$operation" on ${chatIds.length} chats', ); final response = await _dio.post( '/api/v1/chats/batch', @@ -3939,7 +3907,7 @@ class ApiService { String? context, int limit = 5, }) async { - debugPrint('DEBUG: Fetching chat suggestions'); + _traceApi('Fetching chat suggestions'); final queryParams = {'limit': limit}; if (context != null) queryParams['context'] = context; @@ -3959,7 +3927,7 @@ class ApiService { String? category, String? tag, }) async { - debugPrint('DEBUG: Fetching chat templates'); + _traceApi('Fetching chat templates'); final queryParams = {}; if (category != null) queryParams['category'] = category; if (tag != null) queryParams['tag'] = tag; @@ -3981,7 +3949,7 @@ class ApiService { Map? variables, String? title, }) async { - debugPrint('DEBUG: Creating chat from template: $templateId'); + _traceApi('Creating chat from template: $templateId'); final response = await _dio.post( '/api/v1/chats/templates/$templateId/create', data: { diff --git a/lib/core/services/attachment_upload_queue.dart b/lib/core/services/attachment_upload_queue.dart index 23a5c4b..12ca333 100644 --- a/lib/core/services/attachment_upload_queue.dart +++ b/lib/core/services/attachment_upload_queue.dart @@ -5,11 +5,6 @@ import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'attachments/queue'); -} - /// Status of a queued attachment upload enum QueuedAttachmentStatus { pending, uploading, completed, failed, cancelled } @@ -144,8 +139,9 @@ class AttachmentUploadQueue { _prefs ??= await SharedPreferences.getInstance(); await _load(); _startPeriodicProcessing(); - debugPrint( - 'DEBUG: AttachmentUploadQueue initialized with ${_queue.length} items', + DebugLogger.log( + 'AttachmentUploadQueue initialized with ${_queue.length} items', + scope: 'attachments/queue', ); } @@ -223,8 +219,9 @@ class AttachmentUploadQueue { await _save(); _notify(); - debugPrint( - 'DEBUG: Attachment ${item.id} uploaded successfully (fileId=$fileId)', + DebugLogger.log( + 'Attachment ${item.id} uploaded successfully (fileId=$fileId)', + scope: 'attachments/queue', ); } catch (e) { final retries = item.retryCount + 1; @@ -239,8 +236,9 @@ class AttachmentUploadQueue { ); await _save(); _notify(); - debugPrint( + DebugLogger.log( 'WARNING: Attachment ${item.id} failed after $_maxRetries attempts', + scope: 'attachments/queue', ); return; } @@ -257,8 +255,9 @@ class AttachmentUploadQueue { ); await _save(); _notify(); - debugPrint( - 'DEBUG: Scheduled retry for attachment ${item.id} in ${delay.inSeconds}s', + DebugLogger.log( + 'Scheduled retry for attachment ${item.id} in ${delay.inSeconds}s', + scope: 'attachments/queue', ); } } diff --git a/lib/core/services/optimized_storage_service.dart b/lib/core/services/optimized_storage_service.dart index 5871d31..4008b7f 100644 --- a/lib/core/services/optimized_storage_service.dart +++ b/lib/core/services/optimized_storage_service.dart @@ -6,11 +6,6 @@ import '../models/server_config.dart'; import '../models/conversation.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'storage/optimized'); -} - /// Optimized storage service with single secure storage implementation /// Eliminates dual storage overhead and improves performance class OptimizedStorageService { @@ -46,9 +41,15 @@ class OptimizedStorageService { await _secureCredentialStorage.saveAuthToken(token); _cache[_authTokenKey] = token; _cacheTimestamps[_authTokenKey] = DateTime.now(); - debugPrint('DEBUG: Auth token saved and cached'); + DebugLogger.log( + 'Auth token saved and cached', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Failed to save auth token: $e'); + DebugLogger.log( + 'Failed to save auth token: $e', + scope: 'storage/optimized', + ); rethrow; } } @@ -58,7 +59,7 @@ class OptimizedStorageService { if (_isCacheValid(_authTokenKey)) { final cachedToken = _cache[_authTokenKey] as String?; if (cachedToken != null) { - debugPrint('DEBUG: Using cached auth token'); + DebugLogger.log('Using cached auth token', scope: 'storage/optimized'); return cachedToken; } } @@ -71,7 +72,10 @@ class OptimizedStorageService { } return token; } catch (e) { - debugPrint('ERROR: Failed to retrieve auth token: $e'); + DebugLogger.log( + 'Failed to retrieve auth token: $e', + scope: 'storage/optimized', + ); return null; } } @@ -81,9 +85,15 @@ class OptimizedStorageService { await _secureCredentialStorage.deleteAuthToken(); _cache.remove(_authTokenKey); _cacheTimestamps.remove(_authTokenKey); - debugPrint('DEBUG: Auth token deleted and cache cleared'); + DebugLogger.log( + 'Auth token deleted and cache cleared', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Failed to delete auth token: $e'); + DebugLogger.log( + 'Failed to delete auth token: $e', + scope: 'storage/optimized', + ); } } @@ -104,9 +114,15 @@ class OptimizedStorageService { _cache['has_credentials'] = true; _cacheTimestamps['has_credentials'] = DateTime.now(); - debugPrint('DEBUG: Credentials saved via optimized storage'); + DebugLogger.log( + 'Credentials saved via optimized storage', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Failed to save credentials: $e'); + DebugLogger.log( + 'Failed to save credentials: $e', + scope: 'storage/optimized', + ); rethrow; } } @@ -122,7 +138,10 @@ class OptimizedStorageService { return credentials; } catch (e) { - debugPrint('ERROR: Failed to retrieve credentials: $e'); + DebugLogger.log( + 'Failed to retrieve credentials: $e', + scope: 'storage/optimized', + ); return null; } } @@ -132,9 +151,15 @@ class OptimizedStorageService { await _secureCredentialStorage.deleteSavedCredentials(); _cache.remove('has_credentials'); _cacheTimestamps.remove('has_credentials'); - debugPrint('DEBUG: Credentials deleted via optimized storage'); + DebugLogger.log( + 'Credentials deleted via optimized storage', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Failed to delete credentials: $e'); + DebugLogger.log( + 'Failed to delete credentials: $e', + scope: 'storage/optimized', + ); } } @@ -167,9 +192,15 @@ class OptimizedStorageService { _cache['server_config_count'] = configs.length; _cacheTimestamps['server_config_count'] = DateTime.now(); - debugPrint('DEBUG: Server configs saved (${configs.length} configs)'); + DebugLogger.log( + 'Server configs saved (${configs.length} configs)', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Failed to save server configs: $e'); + DebugLogger.log( + 'Failed to save server configs: $e', + scope: 'storage/optimized', + ); rethrow; } } @@ -194,7 +225,10 @@ class OptimizedStorageService { return configs; } catch (e) { - debugPrint('ERROR: Failed to retrieve server configs: $e'); + DebugLogger.log( + 'Failed to retrieve server configs: $e', + scope: 'storage/optimized', + ); return []; } } @@ -275,7 +309,10 @@ class OptimizedStorageService { final decoded = jsonDecode(jsonString) as List; return decoded.map((item) => Conversation.fromJson(item)).toList(); } catch (e) { - debugPrint('ERROR: Failed to retrieve local conversations: $e'); + DebugLogger.log( + 'Failed to retrieve local conversations: $e', + scope: 'storage/optimized', + ); return []; } } @@ -298,11 +335,15 @@ class OptimizedStorageService { final jsonString = jsonEncode(lightweightConversations); await _prefs.setString(_localConversationsKey, jsonString); - debugPrint( - 'DEBUG: Saved ${conversations.length} local conversations (lightweight)', + DebugLogger.log( + 'Saved ${conversations.length} local conversations (lightweight)', + scope: 'storage/optimized', ); } catch (e) { - debugPrint('ERROR: Failed to save local conversations: $e'); + DebugLogger.log( + 'Failed to save local conversations: $e', + scope: 'storage/optimized', + ); } } @@ -331,9 +372,15 @@ class OptimizedStorageService { key.contains('server'), ); - debugPrint('DEBUG: Auth data cleared in batch operation'); + DebugLogger.log( + 'Auth data cleared in batch operation', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Failed to clear auth data: $e'); + DebugLogger.log( + 'Failed to clear auth data: $e', + scope: 'storage/optimized', + ); } } @@ -344,9 +391,12 @@ class OptimizedStorageService { _cache.clear(); _cacheTimestamps.clear(); - debugPrint('DEBUG: All storage cleared'); + DebugLogger.log('All storage cleared', scope: 'storage/optimized'); } catch (e) { - debugPrint('ERROR: Failed to clear all storage: $e'); + DebugLogger.log( + 'Failed to clear all storage: $e', + scope: 'storage/optimized', + ); } } @@ -366,21 +416,30 @@ class OptimizedStorageService { void clearCache() { _cache.clear(); _cacheTimestamps.clear(); - debugPrint('DEBUG: Storage cache cleared'); + DebugLogger.log('Storage cache cleared', scope: 'storage/optimized'); } /// Migration from old storage service (one-time operation) Future migrateFromLegacyStorage() async { try { - debugPrint('DEBUG: Starting migration from legacy storage'); + DebugLogger.log( + 'Starting migration from legacy storage', + scope: 'storage/optimized', + ); // This would be called once during app upgrade // Implementation would depend on the specific migration needs // For now, the SecureCredentialStorage already handles legacy migration - debugPrint('DEBUG: Legacy storage migration completed'); + DebugLogger.log( + 'Legacy storage migration completed', + scope: 'storage/optimized', + ); } catch (e) { - debugPrint('ERROR: Legacy storage migration failed: $e'); + DebugLogger.log( + 'Legacy storage migration failed: $e', + scope: 'storage/optimized', + ); } } diff --git a/lib/core/services/share_receiver_service.dart b/lib/core/services/share_receiver_service.dart index 4cdddaf..34f70bf 100644 --- a/lib/core/services/share_receiver_service.dart +++ b/lib/core/services/share_receiver_service.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:share_handler/share_handler.dart' as sh; @@ -85,7 +85,10 @@ final shareReceiverInitializerProvider = Provider((ref) { maybeProcessPending(); } } catch (e) { - debugPrint('ShareReceiver: failed to get initial shared media: $e'); + DebugLogger.log( + 'ShareReceiver: failed to get initial shared media: $e', + scope: 'share', + ); } }); @@ -98,7 +101,10 @@ final shareReceiverInitializerProvider = Provider((ref) { maybeProcessPending(); } } catch (e) { - debugPrint('ShareReceiver: failed to parse shared media: $e'); + DebugLogger.log( + 'ShareReceiver: failed to parse shared media: $e', + scope: 'share', + ); } }); @@ -197,11 +203,9 @@ Future _processPayload(Ref ref, SharedPayload payload) async { // Do NOT create a server chat here. The chat is created on first send // (with server syncing + title generation) in chat_providers.dart. } catch (e) { - debugPrint('ShareReceiver: failed to process payload: $e'); + DebugLogger.log( + 'ShareReceiver: failed to process payload: $e', + scope: 'share', + ); } } - -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'share'); -} diff --git a/lib/core/services/socket_service.dart b/lib/core/services/socket_service.dart index 1edfab4..9f38f01 100644 --- a/lib/core/services/socket_service.dart +++ b/lib/core/services/socket_service.dart @@ -2,11 +2,6 @@ import 'package:socket_io_client/socket_io_client.dart' as io; import '../models/server_config.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'socket'); -} - class SocketService { final ServerConfig serverConfig; final bool websocketOnly; @@ -97,7 +92,7 @@ class SocketService { _socket = io.io(base, builder.build()); _socket!.on('connect', (_) { - debugPrint('Socket connected: ${_socket!.id}'); + DebugLogger.log('Socket connected: ${_socket!.id}', scope: 'socket'); if (_authToken != null && _authToken!.isNotEmpty) { _socket!.emit('user-join', { 'auth': {'token': _authToken}, @@ -106,15 +101,18 @@ class SocketService { }); _socket!.on('connect_error', (err) { - debugPrint('Socket connect_error: $err'); + DebugLogger.log('Socket connect_error: $err', scope: 'socket'); }); _socket!.on('reconnect_attempt', (attempt) { - debugPrint('Socket reconnect_attempt: $attempt'); + DebugLogger.log('Socket reconnect_attempt: $attempt', scope: 'socket'); }); _socket!.on('reconnect', (attempt) { - debugPrint('Socket reconnected after $attempt attempts'); + DebugLogger.log( + 'Socket reconnected after $attempt attempts', + scope: 'socket', + ); if (_authToken != null && _authToken!.isNotEmpty) { // Best-effort rejoin _socket!.emit('user-join', { @@ -124,11 +122,11 @@ class SocketService { }); _socket!.on('reconnect_failed', (_) { - debugPrint('Socket reconnect_failed'); + DebugLogger.log('Socket reconnect_failed', scope: 'socket'); }); _socket!.on('disconnect', (reason) { - debugPrint('Socket disconnected: $reason'); + DebugLogger.log('Socket disconnected: $reason', scope: 'socket'); }); } diff --git a/lib/core/services/storage_service.dart b/lib/core/services/storage_service.dart index e33857b..8adda7c 100644 --- a/lib/core/services/storage_service.dart +++ b/lib/core/services/storage_service.dart @@ -6,11 +6,6 @@ import '../models/conversation.dart'; import 'secure_credential_storage.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'storage'); -} - class StorageService { final FlutterSecureStorage _secureStorage; final SharedPreferences _prefs; @@ -42,7 +37,10 @@ class StorageService { try { await _secureCredentialStorage.saveAuthToken(token); } catch (e) { - debugPrint('Warning: Enhanced secure storage failed, using fallback: $e'); + DebugLogger.log( + 'Enhanced secure storage failed, using fallback: $e', + scope: 'storage', + ); await _secureStorage.write(key: _authTokenKey, value: token); } } @@ -53,7 +51,10 @@ class StorageService { final token = await _secureCredentialStorage.getAuthToken(); if (token != null) return token; } catch (e) { - debugPrint('Warning: Enhanced secure storage failed, using fallback: $e'); + DebugLogger.log( + 'Enhanced secure storage failed, using fallback: $e', + scope: 'storage', + ); } // Fallback to legacy storage @@ -65,7 +66,10 @@ class StorageService { try { await _secureCredentialStorage.deleteAuthToken(); } catch (e) { - debugPrint('Warning: Failed to delete from enhanced storage: $e'); + DebugLogger.log( + 'Failed to delete from enhanced storage: $e', + scope: 'storage', + ); } await _secureStorage.delete(key: _authTokenKey); @@ -83,8 +87,9 @@ class StorageService { final isSecureAvailable = await _secureCredentialStorage .isSecureStorageAvailable(); if (!isSecureAvailable) { - debugPrint( - 'DEBUG: Enhanced secure storage not available, using legacy storage', + DebugLogger.log( + 'Enhanced secure storage not available, using legacy storage', + scope: 'storage', ); throw Exception('Enhanced secure storage not available'); } @@ -94,9 +99,15 @@ class StorageService { username: username, password: password, ); - debugPrint('DEBUG: Credentials saved using enhanced secure storage'); + DebugLogger.log( + 'Credentials saved using enhanced secure storage', + scope: 'storage', + ); } catch (e) { - debugPrint('Warning: Enhanced secure storage failed, using fallback: $e'); + DebugLogger.log( + 'Enhanced secure storage failed, using fallback: $e', + scope: 'storage', + ); // Fallback to legacy storage try { @@ -120,10 +131,14 @@ class StorageService { ); } - debugPrint('DEBUG: Credentials saved using fallback storage'); + DebugLogger.log( + 'Credentials saved using fallback storage', + scope: 'storage', + ); } catch (fallbackError) { - debugPrint( - 'ERROR: Both enhanced and fallback credential storage failed: $fallbackError', + DebugLogger.log( + 'Both enhanced and fallback credential storage failed: $fallbackError', + scope: 'storage', ); rethrow; } @@ -138,7 +153,10 @@ class StorageService { return credentials; } } catch (e) { - debugPrint('Warning: Enhanced secure storage failed, using fallback: $e'); + DebugLogger.log( + 'Enhanced secure storage failed, using fallback: $e', + scope: 'storage', + ); } // Fallback to legacy storage and migrate if found @@ -153,7 +171,7 @@ class StorageService { if (!decoded.containsKey('serverId') || !decoded.containsKey('username') || !decoded.containsKey('password')) { - debugPrint('Warning: Invalid saved credentials format'); + DebugLogger.log('Invalid saved credentials format', scope: 'storage'); await deleteSavedCredentials(); return null; } @@ -170,16 +188,17 @@ class StorageService { await _secureCredentialStorage.migrateFromOldStorage(legacyCredentials); // If migration successful, clean up legacy storage await _secureStorage.delete(key: _credentialsKey); - debugPrint( - 'DEBUG: Successfully migrated credentials to enhanced storage', + DebugLogger.log( + 'Successfully migrated credentials to enhanced storage', + scope: 'storage', ); } catch (e) { - debugPrint('Warning: Failed to migrate credentials: $e'); + DebugLogger.log('Failed to migrate credentials: $e', scope: 'storage'); } return legacyCredentials; } catch (e) { - debugPrint('Error loading saved credentials: $e'); + DebugLogger.log('Error loading saved credentials: $e', scope: 'storage'); return null; } } @@ -189,7 +208,10 @@ class StorageService { try { await _secureCredentialStorage.deleteSavedCredentials(); } catch (e) { - debugPrint('Warning: Failed to delete from enhanced storage: $e'); + DebugLogger.log( + 'Failed to delete from enhanced storage: $e', + scope: 'storage', + ); } await _secureStorage.delete(key: _credentialsKey); @@ -218,7 +240,10 @@ class StorageService { final decoded = jsonDecode(jsonString); if (decoded is! List) { - debugPrint('Warning: Server configs data is not a list, resetting'); + DebugLogger.log( + 'Server configs data is not a list, resetting', + scope: 'storage', + ); return []; } @@ -232,20 +257,24 @@ class StorageService { item.containsKey('url')) { configs.add(ServerConfig.fromJson(item)); } else { - debugPrint( - 'Warning: Skipping invalid server config: missing required fields', + DebugLogger.log( + 'Skipping invalid server config: missing required fields', + scope: 'storage', ); } } } catch (e) { - debugPrint('Warning: Failed to parse server config: $e'); + DebugLogger.log( + 'Failed to parse server config: $e', + scope: 'storage', + ); // Continue with other configs } } return configs; } catch (e) { - debugPrint('Error loading server configs: $e'); + DebugLogger.log('Error loading server configs: $e', scope: 'storage'); return []; } } @@ -279,8 +308,9 @@ class StorageService { try { final decoded = jsonDecode(jsonString); if (decoded is! List) { - debugPrint( - 'Warning: Local conversations data is not a list, resetting', + DebugLogger.log( + 'Local conversations data is not a list, resetting', + scope: 'storage', ); return []; } @@ -296,20 +326,24 @@ class StorageService { item.containsKey('updatedAt')) { conversations.add(Conversation.fromJson(item)); } else { - debugPrint( - 'Warning: Skipping invalid conversation: missing required fields', + DebugLogger.log( + 'Skipping invalid conversation: missing required fields', + scope: 'storage', ); } } } catch (e) { - debugPrint('Warning: Failed to parse conversation: $e'); + DebugLogger.log('Failed to parse conversation: $e', scope: 'storage'); // Continue with other conversations } } return conversations; } catch (e) { - debugPrint('Error parsing local conversations: $e'); + DebugLogger.log( + 'Error parsing local conversations: $e', + scope: 'storage', + ); return []; } } @@ -319,7 +353,7 @@ class StorageService { final json = conversations.map((c) => c.toJson()).toList(); await _prefs.setString(_localConversationsKey, jsonEncode(json)); } catch (e) { - debugPrint('Error saving local conversations: $e'); + DebugLogger.log('Error saving local conversations: $e', scope: 'storage'); } } @@ -350,21 +384,21 @@ class StorageService { try { await _secureCredentialStorage.clearAll(); } catch (e) { - debugPrint('Warning: Failed to clear enhanced storage: $e'); + DebugLogger.log('Failed to clear enhanced storage: $e', scope: 'storage'); } // Clear legacy storage await _secureStorage.deleteAll(); await _prefs.clear(); - debugPrint('DEBUG: All storage cleared'); + DebugLogger.log('All storage cleared', scope: 'storage'); } // Clear only auth-related data (keeping server configs and other settings) Future clearAuthData() async { await deleteAuthToken(); await deleteSavedCredentials(); - debugPrint('DEBUG: Auth data cleared'); + DebugLogger.log('Auth data cleared', scope: 'storage'); } /// Check if enhanced secure storage is available @@ -372,7 +406,10 @@ class StorageService { try { return await _secureCredentialStorage.isSecureStorageAvailable(); } catch (e) { - debugPrint('Warning: Failed to check enhanced storage availability: $e'); + DebugLogger.log( + 'Failed to check enhanced storage availability: $e', + scope: 'storage', + ); return false; } } diff --git a/lib/core/services/streaming_helper.dart b/lib/core/services/streaming_helper.dart index f432a95..8aa5560 100644 --- a/lib/core/services/streaming_helper.dart +++ b/lib/core/services/streaming_helper.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:convert'; -import 'package:flutter/material.dart' hide debugPrint; +import 'package:flutter/material.dart'; import '../../core/models/chat_message.dart'; import '../../core/services/persistent_streaming_service.dart'; @@ -14,11 +14,6 @@ import '../../shared/widgets/themed_dialogs.dart'; import '../../shared/theme/theme_extensions.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'streaming/helper'); -} - // Keep local verbosity toggle for socket logs const bool kSocketVerboseLogging = false; @@ -83,7 +78,10 @@ StreamSubscription attachUnifiedChunkedStreaming({ ), controller: persistentController, recoveryCallback: () async { - debugPrint('DEBUG: Attempting to recover interrupted stream'); + DebugLogger.log( + 'Attempting to recover interrupted stream', + scope: 'streaming/helper', + ); }, metadata: { 'conversationId': activeConversationId, diff --git a/lib/core/services/user_friendly_error_handler.dart b/lib/core/services/user_friendly_error_handler.dart index 4d1b061..fb5fa2e 100644 --- a/lib/core/services/user_friendly_error_handler.dart +++ b/lib/core/services/user_friendly_error_handler.dart @@ -1,15 +1,10 @@ -import 'package:flutter/foundation.dart' hide debugPrint; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:conduit/l10n/app_localizations.dart'; import '../../shared/theme/theme_extensions.dart'; import 'navigation_service.dart'; import '../utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'errors/user-friendly'); -} - /// User-friendly error messages and recovery actions class UserFriendlyErrorHandler { static final UserFriendlyErrorHandler _instance = @@ -347,9 +342,12 @@ class UserFriendlyErrorHandler { /// Log technical error details for debugging void _logError(dynamic error) { if (kDebugMode) { - debugPrint('ERROR: $error'); + DebugLogger.log('$error', scope: 'errors/user-friendly'); if (error is Error) { - debugPrint('STACK TRACE: ${error.stackTrace}'); + DebugLogger.log( + 'STACK TRACE: ${error.stackTrace}', + scope: 'errors/user-friendly', + ); } } diff --git a/lib/features/chat/providers/chat_providers.dart b/lib/features/chat/providers/chat_providers.dart index 05ee93d..e890028 100644 --- a/lib/features/chat/providers/chat_providers.dart +++ b/lib/features/chat/providers/chat_providers.dart @@ -16,11 +16,6 @@ import '../../tools/providers/tools_providers.dart'; import 'dart:async'; import '../../../core/utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'chat/providers'); -} - const bool kSocketVerboseLogging = false; // Chat messages for current conversation @@ -105,7 +100,10 @@ class ChatMessagesNotifier extends Notifier> { previous, next, ) { - debugPrint('Conversation changed: ${previous?.id} -> ${next?.id}'); + DebugLogger.log( + 'Conversation changed: ${previous?.id} -> ${next?.id}', + scope: 'chat/providers', + ); // Only react when the conversation actually changes if (previous?.id == next?.id) { @@ -1886,7 +1884,10 @@ Please try sending the message again, or try without attachments.''', ); ref.read(chatMessagesProvider.notifier).addMessage(errorMessage); } else if (e.toString().contains('404')) { - debugPrint('DEBUG: Model or endpoint not found (404)'); + DebugLogger.log( + 'Model or endpoint not found (404)', + scope: 'chat/providers', + ); final errorMessage = ChatMessage( id: const Uuid().v4(), role: 'assistant', @@ -2005,7 +2006,10 @@ Future pinConversation( .set(activeConversation!.copyWith(pinned: pinned)); } } catch (e) { - debugPrint('Error ${pinned ? 'pinning' : 'unpinning'} conversation: $e'); + DebugLogger.log( + 'Error ${pinned ? 'pinning' : 'unpinning'} conversation: $e', + scope: 'chat/providers', + ); rethrow; } } @@ -2033,8 +2037,9 @@ Future archiveConversation( // Refresh conversations list to reflect the change ref.invalidate(conversationsProvider); } catch (e) { - debugPrint( + DebugLogger.log( 'Error ${archived ? 'archiving' : 'unarchiving'} conversation: $e', + scope: 'chat/providers', ); // If server operation failed and we archived locally, restore the conversation @@ -2060,7 +2065,7 @@ Future shareConversation(WidgetRef ref, String conversationId) async { return shareId; } catch (e) { - debugPrint('Error sharing conversation: $e'); + DebugLogger.log('Error sharing conversation: $e', scope: 'chat/providers'); rethrow; } } @@ -2081,7 +2086,7 @@ Future cloneConversation(WidgetRef ref, String conversationId) async { // Refresh conversations list to show the new conversation ref.invalidate(conversationsProvider); } catch (e) { - debugPrint('Error cloning conversation: $e'); + DebugLogger.log('Error cloning conversation: $e', scope: 'chat/providers'); rethrow; } } diff --git a/lib/features/chat/views/chat_page.dart b/lib/features/chat/views/chat_page.dart index abd952a..752de0b 100644 --- a/lib/features/chat/views/chat_page.dart +++ b/lib/features/chat/views/chat_page.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart' hide debugPrint; +import 'package:flutter/material.dart'; import 'package:conduit/l10n/app_localizations.dart'; import '../../../core/widgets/error_boundary.dart'; import '../../../shared/widgets/optimized_list.dart'; @@ -48,11 +48,6 @@ import '../../../shared/widgets/model_avatar.dart'; import '../../../core/services/platform_service.dart' as ps; import 'package:flutter/gestures.dart' show DragStartBehavior; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'chat/page'); -} - class ChatPage extends ConsumerStatefulWidget { const ChatPage({super.key}); @@ -251,7 +246,7 @@ class _ChatPageState extends ConsumerState { if (!mounted) return; ref.read(activeConversationProvider.notifier).set(welcomeConv); - debugPrint('Auto-loaded demo conversation'); + DebugLogger.log('Auto-loaded demo conversation', scope: 'chat/page'); return; } @@ -266,7 +261,10 @@ class _ChatPageState extends ConsumerState { break; } - debugPrint('Failed to auto-load demo conversation'); + DebugLogger.log( + 'Failed to auto-load demo conversation', + scope: 'chat/page', + ); } @override @@ -439,18 +437,19 @@ class _ChatPageState extends ConsumerState { ); } catch (e) { if (!mounted) return; - debugPrint('Enqueue upload failed: $e'); + DebugLogger.log('Enqueue upload failed: $e', scope: 'chat/page'); } } } catch (e) { if (!mounted) return; - debugPrint('File selection failed: $e'); + DebugLogger.log('File selection failed: $e', scope: 'chat/page'); } } void _handleImageAttachment({bool fromCamera = false}) async { - debugPrint( - 'DEBUG: Starting image attachment process - fromCamera: $fromCamera', + DebugLogger.log( + 'Starting image attachment process - fromCamera: $fromCamera', + scope: 'chat/page', ); // Check if selected model supports vision @@ -462,23 +461,26 @@ class _ChatPageState extends ConsumerState { final fileService = ref.read(fileAttachmentServiceProvider); if (fileService == null) { - debugPrint('DEBUG: File service is null - cannot proceed'); + DebugLogger.log( + 'File service is null - cannot proceed', + scope: 'chat/page', + ); return; } try { - debugPrint('DEBUG: Picking image...'); + DebugLogger.log('Picking image...', scope: 'chat/page'); final image = fromCamera ? await fileService.takePhoto() : await fileService.pickImage(); if (image == null) { - debugPrint('DEBUG: No image selected'); + DebugLogger.log('No image selected', scope: 'chat/page'); return; } - debugPrint('DEBUG: Image selected: ${image.path}'); + DebugLogger.log('Image selected: ${image.path}', scope: 'chat/page'); final imageSize = await image.length(); - debugPrint('DEBUG: Image size: $imageSize bytes'); + DebugLogger.log('Image size: $imageSize bytes', scope: 'chat/page'); // Validate file size (default 20MB limit like OpenWebUI) if (!validateFileSize(imageSize, 20)) { @@ -495,10 +497,10 @@ class _ChatPageState extends ConsumerState { // Add image to the attachment list ref.read(attachedFilesProvider.notifier).addFiles([image]); - debugPrint('DEBUG: Image added to attachment list'); + DebugLogger.log('Image added to attachment list', scope: 'chat/page'); // Enqueue upload via task queue for unified retry/progress - debugPrint('DEBUG: Enqueueing image upload...'); + DebugLogger.log('Enqueueing image upload...', scope: 'chat/page'); final activeConv = ref.read(activeConversationProvider); try { await ref @@ -510,10 +512,10 @@ class _ChatPageState extends ConsumerState { fileSize: imageSize, ); } catch (e) { - debugPrint('DEBUG: Enqueue image upload failed: $e'); + DebugLogger.log('Enqueue image upload failed: $e', scope: 'chat/page'); } } catch (e) { - debugPrint('DEBUG: Image attachment error: $e'); + DebugLogger.log('Image attachment error: $e', scope: 'chat/page'); if (!mounted) return; } } @@ -886,7 +888,7 @@ class _ChatPageState extends ConsumerState { userMessage.attachmentIds, ); } catch (e) { - debugPrint('Regenerate failed: $e'); + DebugLogger.log('Regenerate failed: $e', scope: 'chat/page'); } } @@ -1389,8 +1391,9 @@ class _ChatPageState extends ConsumerState { .read(activeConversationProvider.notifier) .set(full); } catch (e) { - debugPrint( - 'DEBUG: Failed to refresh conversation: $e', + DebugLogger.log( + 'Failed to refresh conversation: $e', + scope: 'chat/page', ); } } @@ -2101,7 +2104,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { }); }, onDone: () { - debugPrint('DEBUG: VoiceInputSheet stream done'); + DebugLogger.log('VoiceInputSheet stream done', scope: 'chat/page'); setState(() { _isListening = false; }); @@ -2112,7 +2115,10 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { } }, onError: (error) { - debugPrint('DEBUG: VoiceInputSheet stream error: $error'); + DebugLogger.log( + 'VoiceInputSheet stream error: $error', + scope: 'chat/page', + ); setState(() { _isListening = false; }); diff --git a/lib/features/chat/widgets/assistant_message_widget.dart b/lib/features/chat/widgets/assistant_message_widget.dart index 39620ce..d780323 100644 --- a/lib/features/chat/widgets/assistant_message_widget.dart +++ b/lib/features/chat/widgets/assistant_message_widget.dart @@ -1,6 +1,5 @@ -import 'package:flutter/material.dart' hide debugPrint; +import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart' show listEquals; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'dart:convert'; @@ -22,11 +21,6 @@ import 'package:url_launcher/url_launcher_string.dart'; import '../providers/chat_providers.dart' show sendMessage; import '../../../core/utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'chat/assistant'); -} - class AssistantMessageWidget extends ConsumerStatefulWidget { final dynamic message; final bool isStreaming; @@ -76,7 +70,10 @@ class _AssistantMessageWidgetState extends ConsumerState try { await sendMessage(ref, trimmed, null); } catch (err, stack) { - debugPrint('Failed to send follow-up: $err'); + DebugLogger.log( + 'Failed to send follow-up: $err', + scope: 'chat/assistant', + ); debugPrintStack(stackTrace: stack); } } @@ -660,15 +657,6 @@ class _AssistantMessageWidgetState extends ConsumerState const SizedBox(height: Spacing.md), CitationListView(sources: widget.message.sources), ], - - if (hasFollowUps) ...[ - const SizedBox(height: Spacing.md), - FollowUpSuggestionBar( - suggestions: widget.message.followUps, - onSelected: _handleFollowUpTap, - isBusy: widget.isStreaming, - ), - ], ], ), ), @@ -677,6 +665,14 @@ class _AssistantMessageWidgetState extends ConsumerState if (!widget.isStreaming) ...[ const SizedBox(height: Spacing.sm), _buildActionButtons(), + if (hasFollowUps) ...[ + const SizedBox(height: Spacing.md), + FollowUpSuggestionBar( + suggestions: widget.message.followUps, + onSelected: _handleFollowUpTap, + isBusy: widget.isStreaming, + ), + ], ], ], ), @@ -1283,6 +1279,124 @@ class _AssistantMessageWidgetState extends ConsumerState } } +class _AssistantResponseSection extends StatelessWidget { + const _AssistantResponseSection({ + required this.title, + required this.child, + this.icon, + }); + + final String title; + final Widget child; + final IconData? icon; + + @override + Widget build(BuildContext context) { + final theme = context.conduitTheme; + final colorScheme = Theme.of(context).colorScheme; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (icon != null) ...[ + Icon(icon, size: 16, color: theme.buttonPrimary), + const SizedBox(width: Spacing.xs), + ], + Text( + title, + style: TextStyle( + color: theme.textSecondary, + fontSize: AppTypography.bodySmall, + fontWeight: FontWeight.w600, + letterSpacing: 0.15, + ), + ), + ], + ), + const SizedBox(height: Spacing.xs), + Container( + width: double.infinity, + padding: const EdgeInsets.all(Spacing.sm), + decoration: BoxDecoration( + color: theme.cardBackground, + borderRadius: BorderRadius.circular(AppBorderRadius.card), + border: Border.all( + color: theme.cardBorder.withValues(alpha: 0.6), + width: BorderWidth.thin, + ), + boxShadow: [ + BoxShadow( + color: colorScheme.shadow.withValues(alpha: 0.05), + blurRadius: 16, + offset: const Offset(0, 6), + ), + ], + ), + child: child, + ), + ], + ); + } +} + +class _AssistantSuggestionChip extends StatelessWidget { + const _AssistantSuggestionChip({ + required this.label, + this.icon, + this.onPressed, + this.enabled = true, + }); + + final String label; + final IconData? icon; + final VoidCallback? onPressed; + final bool enabled; + + @override + Widget build(BuildContext context) { + final theme = context.conduitTheme; + final effectiveOnPressed = enabled ? onPressed : null; + final iconColor = enabled + ? theme.textSecondary + : theme.textSecondary.withValues(alpha: 0.5); + + final background = theme.cardBackground.withValues( + alpha: enabled ? 0.95 : 0.85, + ); + final borderColor = theme.cardBorder.withValues( + alpha: enabled ? 0.6 : 0.35, + ); + + return RawChip( + avatar: icon != null ? Icon(icon, size: 16, color: iconColor) : null, + label: Text( + label, + style: TextStyle( + color: enabled ? theme.textPrimary : theme.textSecondary, + fontSize: AppTypography.labelMedium, + fontWeight: FontWeight.w500, + letterSpacing: 0.2, + ), + ), + onPressed: effectiveOnPressed, + padding: const EdgeInsets.symmetric( + horizontal: Spacing.sm, + vertical: Spacing.xxs, + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + backgroundColor: background, + disabledColor: background, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppBorderRadius.pill), + side: BorderSide(color: borderColor, width: BorderWidth.thin), + ), + ); + } +} + class StatusHistoryTimeline extends StatelessWidget { const StatusHistoryTimeline({super.key, required this.updates}); @@ -1290,39 +1404,24 @@ class StatusHistoryTimeline extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = context.conduitTheme; if (updates.isEmpty) { return const SizedBox.shrink(); } - return Container( - width: double.infinity, - padding: const EdgeInsets.all(Spacing.sm), - decoration: BoxDecoration( - color: theme.surfaceContainer.withValues(alpha: 0.6), - borderRadius: BorderRadius.circular(AppBorderRadius.md), - border: Border.all( - color: theme.dividerColor.withValues(alpha: 0.6), - width: BorderWidth.thin, - ), - ), + return _AssistantResponseSection( + title: 'Status updates', + icon: Icons.sync_alt, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'Status updates', - style: TextStyle( - color: theme.textPrimary, - fontWeight: FontWeight.w600, - fontSize: AppTypography.bodyLarge, + const SizedBox(height: Spacing.xs), + for (var index = 0; index < updates.length; index++) + Padding( + padding: EdgeInsets.only( + bottom: index == updates.length - 1 ? 0 : Spacing.xs, + ), + child: _StatusHistoryEntry(update: updates[index]), ), - ), - const SizedBox(height: Spacing.sm), - ...List.generate(updates.length, (index) { - final update = updates[index]; - final isLast = index == updates.length - 1; - return _StatusHistoryEntry(update: update, isLast: isLast); - }), ], ), ); @@ -1330,10 +1429,9 @@ class StatusHistoryTimeline extends StatelessWidget { } class _StatusHistoryEntry extends StatelessWidget { - const _StatusHistoryEntry({required this.update, required this.isLast}); + const _StatusHistoryEntry({required this.update}); final ChatStatusUpdate update; - final bool isLast; Color _indicatorColor(ConduitThemeExtension theme) { if (update.done == false) { @@ -1372,142 +1470,159 @@ class _StatusHistoryEntry extends StatelessWidget { } } - return Padding( - padding: const EdgeInsets.only(bottom: Spacing.sm), - child: Row( + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric( + horizontal: Spacing.sm, + vertical: Spacing.sm, + ), + decoration: BoxDecoration( + color: theme.cardBackground.withValues(alpha: 0.92), + borderRadius: BorderRadius.circular(AppBorderRadius.md), + border: Border.all( + color: theme.cardBorder.withValues(alpha: 0.5), + width: BorderWidth.thin, + ), + ), + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( + Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon(_indicatorIcon(), size: 18, color: indicatorColor), - if (!isLast) - Container( - margin: const EdgeInsets.only(top: Spacing.xxs), - width: 2, - height: 32, - color: theme.dividerColor.withValues(alpha: 0.5), + Icon(_indicatorIcon(), size: 16, color: indicatorColor), + const SizedBox(width: Spacing.sm), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + description, + style: TextStyle( + fontSize: AppTypography.bodySmall, + color: theme.textSecondary, + fontWeight: update.done == true + ? FontWeight.w600 + : FontWeight.w500, + ), + ), + if (update.count != null) + Padding( + padding: const EdgeInsets.only(top: Spacing.xxs), + child: Text( + update.count == 1 + ? 'Retrieved 1 source' + : 'Retrieved ${update.count} sources', + style: TextStyle( + color: theme.textSecondary, + fontSize: AppTypography.labelSmall, + fontWeight: FontWeight.w500, + ), + ), + ), + if (timestamp != null) + Padding( + padding: const EdgeInsets.only(top: Spacing.xxs), + child: Text( + _formatTimestamp(timestamp), + style: TextStyle( + color: theme.textSecondary.withValues(alpha: 0.8), + fontSize: AppTypography.labelSmall, + ), + ), + ), + ], ), + ), ], ), - const SizedBox(width: Spacing.sm), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - description, - style: TextStyle( - fontSize: AppTypography.bodyMedium, - color: theme.textPrimary, - fontWeight: update.done == true - ? FontWeight.w600 - : FontWeight.w500, - ), - ), - if (update.count != null) - Padding( - padding: const EdgeInsets.only(top: Spacing.xxs), - child: Text( - update.count == 1 - ? 'Retrieved 1 source' - : 'Retrieved ${update.count} sources', - style: TextStyle( - color: theme.textSecondary, - fontSize: AppTypography.labelSmall, - ), - ), - ), - if (timestamp != null) - Padding( - padding: const EdgeInsets.only(top: Spacing.xxs), - child: Text( - _formatTimestamp(timestamp), - style: TextStyle( - color: theme.textSecondary, - fontSize: AppTypography.labelSmall, - ), - ), - ), - if (queries.isNotEmpty) - Padding( - padding: const EdgeInsets.only(top: Spacing.xxs), - child: Wrap( - spacing: Spacing.xs, - runSpacing: Spacing.xs, - children: queries.map((query) { - return ActionChip( - label: Text(query), - avatar: const Icon(Icons.search, size: 16), - onPressed: () { - _launchUri( - 'https://www.google.com/search?q=${Uri.encodeComponent(query)}', - ); - }, - ); - }).toList(), - ), - ), - if (update.urls.isNotEmpty) - Padding( - padding: const EdgeInsets.only(top: Spacing.xxs), - child: Wrap( - spacing: Spacing.xs, - runSpacing: Spacing.xs, - children: update.urls.map((url) { - return OutlinedButton.icon( - onPressed: () => _launchUri(url), - icon: const Icon(Icons.open_in_new, size: 16), - label: Text( - Uri.tryParse(url)?.host ?? 'Link', - overflow: TextOverflow.ellipsis, - ), - ); - }).toList(), - ), - ), - if (update.items.isNotEmpty) - Padding( - padding: const EdgeInsets.only(top: Spacing.xxs), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: update.items.map((item) { - final title = item.title?.isNotEmpty == true - ? item.title! - : item.link ?? 'Result'; - return Padding( - padding: const EdgeInsets.only(bottom: Spacing.xxs), - child: InkWell( - onTap: item.link != null - ? () => _launchUri(item.link!) - : null, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Icon(Icons.link, size: 16), - const SizedBox(width: Spacing.xxs), - Expanded( - child: Text( - title, - style: TextStyle( - color: item.link != null - ? theme.buttonPrimary - : theme.textSecondary, - decoration: item.link != null - ? TextDecoration.underline - : TextDecoration.none, - ), - ), - ), - ], - ), - ), - ); - }).toList(), - ), - ), - ], + if (queries.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: Spacing.sm), + child: Wrap( + spacing: Spacing.xs, + runSpacing: Spacing.xs, + children: queries.map((query) { + return _AssistantSuggestionChip( + label: query, + icon: Icons.search, + onPressed: () { + _launchUri( + 'https://www.google.com/search?q=${Uri.encodeComponent(query)}', + ); + }, + ); + }).toList(), + ), + ), + if (update.urls.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: Spacing.sm), + child: Wrap( + spacing: Spacing.xs, + runSpacing: Spacing.xs, + children: update.urls.map((url) { + final host = Uri.tryParse(url)?.host ?? 'Link'; + return _AssistantSuggestionChip( + label: host, + icon: Icons.open_in_new, + onPressed: () => _launchUri(url), + ); + }).toList(), + ), + ), + if (update.items.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: Spacing.sm), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: update.items.map((item) { + final title = item.title?.isNotEmpty == true + ? item.title! + : item.link ?? 'Result'; + return Padding( + padding: const EdgeInsets.only(bottom: Spacing.xs), + child: InkWell( + onTap: item.link != null + ? () => _launchUri(item.link!) + : null, + borderRadius: BorderRadius.circular(AppBorderRadius.sm), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: Spacing.xxs, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.link, + size: 16, + color: theme.textSecondary, + ), + const SizedBox(width: Spacing.xs), + Expanded( + child: Text( + title, + style: TextStyle( + color: item.link != null + ? theme.buttonPrimary + : theme.textSecondary, + decoration: item.link != null + ? TextDecoration.underline + : TextDecoration.none, + fontSize: AppTypography.bodySmall, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ), + ), + ); + }).toList(), + ), ), - ), ], ), ); @@ -1772,7 +1887,7 @@ class CitationListView extends StatelessWidget { } } -class FollowUpSuggestionBar extends StatefulWidget { +class FollowUpSuggestionBar extends StatelessWidget { const FollowUpSuggestionBar({ super.key, required this.suggestions, @@ -1784,149 +1899,37 @@ class FollowUpSuggestionBar extends StatefulWidget { final ValueChanged onSelected; final bool isBusy; - @override - State createState() => _FollowUpSuggestionBarState(); -} - -class _FollowUpSuggestionBarState extends State - with SingleTickerProviderStateMixin { - late final AnimationController _controller; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 520), - ); - if (widget.suggestions.isNotEmpty) { - _controller.forward(); - } - } - - @override - void didUpdateWidget(covariant FollowUpSuggestionBar oldWidget) { - super.didUpdateWidget(oldWidget); - if (!listEquals(oldWidget.suggestions, widget.suggestions)) { - if (widget.suggestions.isEmpty) { - _controller.reset(); - } else { - _controller.forward(from: 0); - } - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { - final theme = context.conduitTheme; - if (widget.suggestions.isEmpty) { + final trimmedSuggestions = suggestions + .map((s) => s.trim()) + .where((s) => s.isNotEmpty) + .toList(growable: false); + + if (trimmedSuggestions.isEmpty) { return const SizedBox.shrink(); } - final Animation headerAnimation = CurvedAnimation( - parent: _controller, - curve: const Interval(0, 0.35, curve: Curves.easeOutCubic), - ); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AnimatedBuilder( - animation: headerAnimation, - builder: (context, child) { - return Opacity( - opacity: headerAnimation.value, - child: Transform.translate( - offset: Offset(0, (1 - headerAnimation.value) * 10), - child: child, - ), - ); - }, - child: Text( - 'Try next', - style: TextStyle( - color: theme.textPrimary, - fontWeight: FontWeight.w600, - fontSize: AppTypography.bodyLarge, - ), + return _AssistantResponseSection( + title: 'Suggested next steps', + icon: Icons.auto_awesome, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: Spacing.xs), + Wrap( + spacing: Spacing.xs, + runSpacing: Spacing.xs, + children: [ + for (final suggestion in trimmedSuggestions) + _AssistantSuggestionChip( + label: suggestion, + onPressed: isBusy ? null : () => onSelected(suggestion), + enabled: !isBusy, + ), + ], ), - ), - const SizedBox(height: Spacing.xs), - Wrap( - spacing: Spacing.xs, - runSpacing: Spacing.xs, - children: [ - for (var i = 0; i < widget.suggestions.length; i++) - _AnimatedSuggestionChip( - controller: _controller, - index: i, - total: widget.suggestions.length, - isBusy: widget.isBusy, - suggestion: widget.suggestions[i], - onSelected: widget.onSelected, - ), - ], - ), - ], - ); - } -} - -class _AnimatedSuggestionChip extends StatelessWidget { - const _AnimatedSuggestionChip({ - required this.controller, - required this.index, - required this.total, - required this.isBusy, - required this.suggestion, - required this.onSelected, - }); - - final AnimationController controller; - final int index; - final int total; - final bool isBusy; - final String suggestion; - final ValueChanged onSelected; - - Interval _intervalForIndex() { - if (total <= 1) { - return const Interval(0.0, 0.8, curve: Curves.easeOutCubic); - } - final double step = 0.6 / total; - final double start = (index * step).clamp(0.0, 0.8); - final double end = (start + 0.4).clamp(0.2, 1.0); - return Interval(start, end, curve: Curves.easeOutCubic); - } - - @override - Widget build(BuildContext context) { - final animation = CurvedAnimation( - parent: controller, - curve: _intervalForIndex(), - ); - - return AnimatedBuilder( - animation: animation, - builder: (context, child) { - final double t = animation.value; - return Opacity( - opacity: t, - child: Transform.translate( - offset: Offset(0, (1 - t) * 12), - child: Transform.scale(scale: 0.95 + (t * 0.05), child: child), - ), - ); - }, - child: FilledButton.tonal( - onPressed: isBusy ? null : () => onSelected(suggestion), - child: Text(suggestion), + ], ), ); } @@ -1937,6 +1940,6 @@ Future _launchUri(String url) async { try { await launchUrlString(url, mode: LaunchMode.externalApplication); } catch (err) { - debugPrint('Unable to open url $url: $err'); + DebugLogger.log('Unable to open url $url: $err', scope: 'chat/assistant'); } } diff --git a/lib/features/chat/widgets/enhanced_image_attachment.dart b/lib/features/chat/widgets/enhanced_image_attachment.dart index ad421db..1fa3f0b 100644 --- a/lib/features/chat/widgets/enhanced_image_attachment.dart +++ b/lib/features/chat/widgets/enhanced_image_attachment.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/material.dart' hide debugPrint; +import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -14,11 +14,6 @@ import '../../../core/providers/app_providers.dart'; import '../../auth/providers/unified_auth_providers.dart'; import '../../../core/utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'chat/image-attachment'); -} - // Simple global cache to prevent reloading final _globalImageCache = {}; final _globalLoadingStates = {}; @@ -696,7 +691,10 @@ class FullScreenImageViewer extends ConsumerWidget { await SharePlus.instance.share(ShareParams(files: [XFile(file.path)])); } catch (e) { // Swallowing UI feedback per requirements; keep a log for debugging - debugPrint('Failed to share image: $e'); + DebugLogger.log( + 'Failed to share image: $e', + scope: 'chat/image-attachment', + ); } } } diff --git a/lib/main.dart b/lib/main.dart index 5e9fedc..e2489b3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:developer' as developer; -import 'package:flutter/material.dart' hide debugPrint; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'core/widgets/error_boundary.dart'; @@ -22,11 +22,6 @@ import 'core/providers/app_startup_providers.dart'; developer.TimelineTask? _startupTimeline; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'app/main'); -} - void main() { runZonedGuarded( () async { @@ -41,7 +36,7 @@ void main() { ); final stack = details.stack; if (stack != null) { - debugPrint(stack.toString()); + debugPrintStack(stackTrace: stack); } }; WidgetsBinding.instance.platformDispatcher.onError = (error, stack) { @@ -51,7 +46,7 @@ void main() { error: error, stackTrace: stack, ); - debugPrint(stack.toString()); + debugPrintStack(stackTrace: stack); return true; }; @@ -108,7 +103,7 @@ void main() { error: error, stackTrace: stack, ); - debugPrint(stack.toString()); + debugPrintStack(stackTrace: stack); }, ); } diff --git a/lib/shared/services/tasks/task_queue.dart b/lib/shared/services/tasks/task_queue.dart index 9aa1db5..dbaaf10 100644 --- a/lib/shared/services/tasks/task_queue.dart +++ b/lib/shared/services/tasks/task_queue.dart @@ -9,11 +9,6 @@ import 'outbound_task.dart'; import 'task_worker.dart'; import '../../../core/utils/debug_logger.dart'; -void debugPrint(String? message, {int? wrapWidth}) { - if (message == null) return; - DebugLogger.fromLegacy(message, scope: 'tasks/queue'); -} - final taskQueueProvider = NotifierProvider>( TaskQueueNotifier.new, @@ -61,7 +56,7 @@ class TaskQueueNotifier extends Notifier> { // Kick processing after load _process(); } catch (e) { - debugPrint('DEBUG: Failed to load task queue: $e'); + DebugLogger.log('Failed to load task queue: $e', scope: 'tasks/queue'); } } @@ -83,7 +78,7 @@ class TaskQueueNotifier extends Notifier> { final raw = retained.map((t) => t.toJson()).toList(growable: false); await prefs.setString(_prefsKey, jsonEncode(raw)); } catch (e) { - debugPrint('DEBUG: Failed to persist task queue: $e'); + DebugLogger.log('Failed to persist task queue: $e', scope: 'tasks/queue'); } } @@ -262,7 +257,10 @@ class TaskQueueNotifier extends Notifier> { t, ]; } catch (e, st) { - debugPrint('Task failed (${task.runtimeType}): $e\n$st'); + DebugLogger.log( + 'Task failed (${task.runtimeType}): $e\n$st', + scope: 'tasks/queue', + ); state = [ for (final t in state) if (t.id == task.id)