ApiClient constructor

ApiClient(
  1. String initialBaseUrl
)

Implementation

ApiClient(String initialBaseUrl) {
  _baseUrl = initialBaseUrl; // Set initial base URL
  _dio = Dio(BaseOptions(
    baseUrl: _baseUrl,
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    },
    connectTimeout: const Duration(seconds: 45),
    receiveTimeout: const Duration(seconds: 45),
  ));

  // Use platform-specific HttpClientAdapter
  // _dio.httpClientAdapter = getHttpClientAdapter();

  _dio.interceptors.add(ApiMonitorInterceptor());
  _dio.interceptors.add(InterceptorsWrapper(
    onRequest: (options, handler) {
      final token = storageUtils.getToken();
      final roleId = storageUtils.getRoleId() ?? '0';
      final masterRoleTypeId = storageUtils.getMasterRoleTypeId() ?? '0';
      final aclPositionMappingId = storageUtils.getAclPositionMapping() ?? '0';
      if (token != null) {
        options.headers['Authorization'] = token;
        // options.headers['Authorization'] = '$token*##*$roleId*##*$masterRoleTypeId*##*$aclPositionMappingId*##*';
      }

      if (kDebugMode) {
        logger.i('Request: ${options.method} ${options.uri}');
        if (options.headers.isNotEmpty) {
          logger.d('Headers: ${jsonEncode(options.headers)}');
        }
        if (options.queryParameters.isNotEmpty) {
          logger.d('Query Parameters: ${options.queryParameters}');
        }
        if (options.data != null) {
          logger.d('Request Body: ${options.data}');
        }
      }
      return handler.next(options); //continue
    },
    onResponse: (response, handler) {
      if (kDebugMode) {
        logger.w('Response: ${jsonEncode(response.data)}');
      }
      return handler.next(response); // continue
    },
    onError: (DioException error, handler) {
      final apiName = error.requestOptions.uri.toString(); // Extract API name
      final userFriendlyMessage = ErrorParser.parseErrorMessage(error);

      // Safe logging that won't crash on HTML response or null data
      String errorPayload;
      try {
        if (error.response?.data != null) {
          errorPayload = error.response!.data is String
              ? error.response!.data.toString()
              : jsonEncode(error.response!.data);
        } else {
          errorPayload = error.message ?? 'No additional details';
        }
      } catch (_) {
        errorPayload = 'Unencodable response data';
      }

      logger.e('\x1B[31m[ERROR] API: $apiName - Status: ${error.response?.statusCode} - $errorPayload\x1B[0m');

      if (error.response != null) {
        final statusCode = error.response?.statusCode;

        switch (statusCode) {
          case 401: // Unauthorized
            break;
          case 403: // Forbidden
            _handleInvalidToken(userFriendlyMessage);
            break;

          case 440: // Login Timeout (non-standard)
            _handleSessionExpired();
            break;

          case 400: // Bad Request
            break;
          case 404: // Not Found
            break;
          case 500: // Internal Server Error
          case 502: // Bad Gateway
          case 503: // Service Unavailable
          case 504: // Gateway Timeout
            // AppUtils.showSnackBar(userFriendlyMessage);
            break;

          default:
            print('\x1B[31mUnhandled status code: $statusCode - $userFriendlyMessage\x1B[0m');
            AppUtils.showSnackBar(userFriendlyMessage);
            break;
        }
      } else {
        // No response (timeout, offline, socket exception, etc.)
        print('\x1B[31mNetwork error: ${error.message} - $userFriendlyMessage\x1B[0m');
        AppUtils.showSnackBar(userFriendlyMessage);
      }

      // Return the modified error with copyWith to carry our clean message!
      // This ensures existing code catching DioException receives the userFriendlyMessage
      // without breaking the catch block types!
      final updatedError = error.copyWith(message: userFriendlyMessage);
      return handler.next(updatedError);
    },
  ));
}