Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 41 additions & 22 deletions lib/providers/api_provider.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io';

import 'package:chopper/chopper.dart';
import 'package:http/http.dart' as http;
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:punycoder/punycoder.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

Expand Down Expand Up @@ -74,6 +75,12 @@ class _TempJellyRequest implements Interceptor {
}
}

final int _maxRetries = 3;

bool _isConnectionError(Object e) {
return e is IOException || e is TimeoutException;
}

class JellyRequest implements Interceptor {
JellyRequest(this.ref);

Expand All @@ -82,31 +89,43 @@ class JellyRequest implements Interceptor {
@override
FutureOr<Response<BodyType>> intercept<BodyType>(Chain<BodyType> chain) async {
final connectivityNotifier = ref.read(connectivityStatusProvider.notifier);
String? serverUrl = ref.read(serverUrlProvider);
final serverUrl = ref.read(serverUrlProvider);

try {
if (serverUrl?.isEmpty == true || serverUrl == null) throw const HttpException("Failed to connect");

//Use current logged in user otherwise use the authProvider
var loginModel = ref.read(userProvider)?.credentials ?? ref.read(authProvider).serverLoginModel?.tempCredentials;

if (loginModel == null) throw UnimplementedError();
if (serverUrl == null || serverUrl.isEmpty) {
throw const HttpException('No server URL provided');
}

var headers = loginModel.header(ref);
final Response<BodyType> response = await chain.proceed(
applyHeaders(
chain.request.copyWith(
baseUri: Uri.parse(serverUrl),
),
headers),
);
// Use current logged in user otherwise use the authProvider
final loginModel = ref.read(userProvider)?.credentials ?? ref.read(authProvider).serverLoginModel?.tempCredentials;
if (loginModel == null) {
throw UnimplementedError();
}

connectivityNotifier.checkConnectivity();
return response;
} catch (e) {
connectivityNotifier.onStateChange([ConnectivityResult.none]);
throw Exception('Failed to make request\n$e');
final headers = loginModel.header(ref);

for (var attempt = 0; attempt <= _maxRetries; attempt++) {
try {
final response = await chain.proceed(
applyHeaders(
chain.request.copyWith(baseUri: Uri.parse(serverUrl)),
headers,
),
);

connectivityNotifier.checkConnectivity();
return response;
} catch (e) {
if (!_isConnectionError(e) || attempt == _maxRetries) {
connectivityNotifier.onStateChange([ConnectivityResult.none]);
rethrow;
}

final delay = Duration(milliseconds: 200 * (attempt + 1));
log('Connection failed (attempt ${attempt + 1}/$_maxRetries), retrying in ${delay.inMilliseconds}ms: $e');
await Future.delayed(delay);
}
}
throw StateError('Unexpected state in JellyRequest.intercept');
}
}

Expand Down
Loading