1+ import 'dart:async' ;
12import 'dart:developer' ;
23import 'dart:io' ;
34
45import 'package:chopper/chopper.dart' ;
5- import 'package:http/http.dart' as http;
66import 'package:connectivity_plus/connectivity_plus.dart' ;
77import 'package:flutter_riverpod/flutter_riverpod.dart' ;
8+ import 'package:http/http.dart' as http;
89import 'package:punycoder/punycoder.dart' ;
910import 'package:riverpod_annotation/riverpod_annotation.dart' ;
1011
@@ -74,6 +75,12 @@ class _TempJellyRequest implements Interceptor {
7475 }
7576}
7677
78+ final int _maxRetries = 3 ;
79+
80+ bool _isConnectionError (Object e) {
81+ return e is IOException || e is TimeoutException ;
82+ }
83+
7784class JellyRequest implements Interceptor {
7885 JellyRequest (this .ref);
7986
@@ -82,31 +89,43 @@ class JellyRequest implements Interceptor {
8289 @override
8390 FutureOr <Response <BodyType >> intercept <BodyType >(Chain <BodyType > chain) async {
8491 final connectivityNotifier = ref.read (connectivityStatusProvider.notifier);
85- String ? serverUrl = ref.read (serverUrlProvider);
92+ final serverUrl = ref.read (serverUrlProvider);
8693
87- try {
88- if (serverUrl? .isEmpty == true || serverUrl == null ) throw const HttpException ("Failed to connect" );
89-
90- //Use current logged in user otherwise use the authProvider
91- var loginModel = ref.read (userProvider)? .credentials ?? ref.read (authProvider).serverLoginModel? .tempCredentials;
92-
93- if (loginModel == null ) throw UnimplementedError ();
94+ if (serverUrl == null || serverUrl.isEmpty) {
95+ throw const HttpException ('No server URL provided' );
96+ }
9497
95- var headers = loginModel.header (ref);
96- final Response <BodyType > response = await chain.proceed (
97- applyHeaders (
98- chain.request.copyWith (
99- baseUri: Uri .parse (serverUrl),
100- ),
101- headers),
102- );
98+ // Use current logged in user otherwise use the authProvider
99+ final loginModel = ref.read (userProvider)? .credentials ?? ref.read (authProvider).serverLoginModel? .tempCredentials;
100+ if (loginModel == null ) {
101+ throw UnimplementedError ();
102+ }
103103
104- connectivityNotifier.checkConnectivity ();
105- return response;
106- } catch (e) {
107- connectivityNotifier.onStateChange ([ConnectivityResult .none]);
108- throw Exception ('Failed to make request\n $e ' );
104+ final headers = loginModel.header (ref);
105+
106+ for (var attempt = 0 ; attempt <= _maxRetries; attempt++ ) {
107+ try {
108+ final response = await chain.proceed (
109+ applyHeaders (
110+ chain.request.copyWith (baseUri: Uri .parse (serverUrl)),
111+ headers,
112+ ),
113+ );
114+
115+ connectivityNotifier.checkConnectivity ();
116+ return response;
117+ } catch (e) {
118+ if (! _isConnectionError (e) || attempt == _maxRetries) {
119+ connectivityNotifier.onStateChange ([ConnectivityResult .none]);
120+ rethrow ;
121+ }
122+
123+ final delay = Duration (milliseconds: 200 * (attempt + 1 ));
124+ log ('Connection failed (attempt ${attempt + 1 }/$_maxRetries ), retrying in ${delay .inMilliseconds }ms: $e ' );
125+ await Future .delayed (delay);
126+ }
109127 }
128+ throw StateError ('Unexpected state in JellyRequest.intercept' );
110129 }
111130}
112131
0 commit comments