import 'dart:typed_data'; import 'dart:convert'; import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/service/connectivity_status.service.dart'; import 'package:paperless_mobile/core/type/types.dart'; import 'package:paperless_mobile/di_initializer.dart'; import 'package:http/http.dart'; import 'package:injectable/injectable.dart'; /// /// Convenience class which handles timeout errors. /// @Injectable(as: BaseClient) @Named("timeoutClient") class TimeoutClient implements BaseClient { final ConnectivityStatusService connectivityStatusService; static const Duration requestTimeout = Duration(seconds: 25); TimeoutClient(this.connectivityStatusService); @override Future send(BaseRequest request) async { return getIt().send(request).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ); } @override void close() { getIt().close(); } @override Future delete( Uri url, { Map? headers, Object? body, Encoding? encoding, }) async { await _handleOfflineState(); return _handle400Error( await getIt() .delete(url, headers: headers, body: body, encoding: encoding) .timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future get( Uri url, { Map? headers, }) async { await _handleOfflineState(); return _handle400Error( await getIt().get(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future head( Uri url, { Map? headers, }) async { await _handleOfflineState(); return _handle400Error( await getIt().head(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future patch( Uri url, { Map? headers, Object? body, Encoding? encoding, }) async { await _handleOfflineState(); return _handle400Error( await getIt() .patch(url, headers: headers, body: body, encoding: encoding) .timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future post( Uri url, { Map? headers, Object? body, Encoding? encoding, }) async { await _handleOfflineState(); return _handle400Error( await getIt() .post(url, headers: headers, body: body, encoding: encoding) .timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future put( Uri url, { Map? headers, Object? body, Encoding? encoding, }) async { await _handleOfflineState(); return _handle400Error( await getIt() .put(url, headers: headers, body: body, encoding: encoding) .timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future read( Uri url, { Map? headers, }) async { await _handleOfflineState(); return getIt().read(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ); } @override Future readBytes( Uri url, { Map? headers, }) async { await _handleOfflineState(); return getIt().readBytes(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ); } Response _handle400Error(Response response) { if (response.statusCode == 400) { // try to parse contained error message, otherwise return response final JSON json = jsonDecode(utf8.decode(response.bodyBytes)); final PaperlessValidationErrors errorMessages = {}; for (final entry in json.entries) { if (entry.value is List) { errorMessages.putIfAbsent( entry.key, () => (entry.value as List).cast().first); } else if (entry.value is String) { errorMessages.putIfAbsent(entry.key, () => entry.value); } else { errorMessages.putIfAbsent(entry.key, () => entry.value.toString()); } } throw errorMessages; } return response; } Future _handleOfflineState() async { if (!(await connectivityStatusService.isConnectedToInternet())) { throw const ErrorMessage(ErrorCode.deviceOffline); } } }