diff --git a/lib/core/extensions/task_extensions.dart b/lib/core/extensions/task_extensions.dart
new file mode 100644
index 000000000..9f13d1b85
--- /dev/null
+++ b/lib/core/extensions/task_extensions.dart
@@ -0,0 +1,14 @@
+import 'package:fpdart/fpdart.dart';
+
+extension TaskX on Task {
+ /// Ensure that this task takes at least [duration] to complete.
+ ///
+ /// If the task completes before [duration], the returned task will
+ /// wait for the remaining time before returning the result.
+ Task withMinimumDuration(Duration duration) => Task(() async {
+ final minimumDurationTask = Future.delayed(duration);
+ final result = await run();
+ await minimumDurationTask;
+ return result;
+ });
+}
diff --git a/lib/core/throttler.dart b/lib/core/throttler.dart
index 1c1207bfa..8ed26c9d0 100644
--- a/lib/core/throttler.dart
+++ b/lib/core/throttler.dart
@@ -2,7 +2,8 @@ import 'package:fpdart/fpdart.dart';
/// A utility class for throttling the execution of asynchronous tasks.
///
-/// This class allows you to limit the rate at which a task is executed by
+/// {@template throttler}
+/// A [Throttler] allows you to limit the rate at which a task is executed by
/// ensuring that only one task runs through the [Throttler] at any given time.
/// If a task is already running, subsequent calls to execute a task will
/// instead return the currently running task.
@@ -36,7 +37,11 @@ import 'package:fpdart/fpdart.dart';
/// // 5
/// }
/// ```
+/// {@endtemplate}
class Throttler {
+ /// Creates a new [Throttler] instance for [Future]s that return a [T].
+ ///
+ /// {@macro throttler}
Throttler();
Future? _storedTask;
@@ -52,9 +57,10 @@ class Throttler {
}
extension ThrottlerX on Task {
- /// Throttles this task using the given [throttler].
+ /// Adds the given [throttler] to this task.
///
/// If no task is currently running through the [throttler], starts this task
/// and stores it. Otherwise, returns the currently running task.
- Future runThrottled(Throttler throttler) => throttler.throttle(this);
+ Task throttleWith(Throttler throttler) =>
+ Task(() => throttler.throttle(this));
}
diff --git a/lib/service_locator.dart b/lib/service_locator.dart
index 2d4e52d7f..34bb7c351 100644
--- a/lib/service_locator.dart
+++ b/lib/service_locator.dart
@@ -351,7 +351,11 @@ void initRegister() {
void initHttp() {
ignoreValue(
sl.registerSingleton(
- RetryAuthenticator.uninitialized(serviceLocator: sl),
+ RetryAuthenticator.uninitialized(
+ repository: sl(),
+ cubit: sl(),
+ logger: sl(),
+ ),
),
);
diff --git a/lib/src/authentication/authentication.dart b/lib/src/authentication/authentication.dart
index 2d091e19e..a4c424f29 100644
--- a/lib/src/authentication/authentication.dart
+++ b/lib/src/authentication/authentication.dart
@@ -1,5 +1,6 @@
export 'bloc/authentication_cubit.dart';
export 'http_utils/authentication_interceptor.dart';
export 'http_utils/retry_authenticator.dart';
+export 'http_utils/with_bearer_token.dart';
export 'models/authentication_info.dart';
export 'repository.dart';
diff --git a/lib/src/authentication/http_utils/authentication_interceptor.dart b/lib/src/authentication/http_utils/authentication_interceptor.dart
index 75b1a47ff..c30cdc0f5 100644
--- a/lib/src/authentication/http_utils/authentication_interceptor.dart
+++ b/lib/src/authentication/http_utils/authentication_interceptor.dart
@@ -11,14 +11,11 @@ class AuthenticationInterceptor implements chopper.RequestInterceptor {
final AuthenticationRepository repository;
@override
- FutureOr onRequest(chopper.Request request) {
- return repository.getAuthenticationInfo().match(
- () => request,
- (authenticationInfo) {
- final updatedHeaders = Map.of(request.headers);
- updatedHeaders['Authorization'] = 'Bearer ${authenticationInfo.token}';
- return request.copyWith(headers: updatedHeaders);
- },
- ).run();
+ FutureOr onRequest(chopper.Request originalRequest) {
+ return repository
+ .getAuthenticationToken()
+ .map(originalRequest.withBearerToken)
+ .getOrElse(() => originalRequest)
+ .run();
}
}
diff --git a/lib/src/authentication/http_utils/retry_authenticator.dart b/lib/src/authentication/http_utils/retry_authenticator.dart
index 15cca704c..fd84eee3c 100644
--- a/lib/src/authentication/http_utils/retry_authenticator.dart
+++ b/lib/src/authentication/http_utils/retry_authenticator.dart
@@ -1,23 +1,26 @@
import 'dart:async';
-import 'package:chopper/chopper.dart' as chopper;
+import 'package:chopper/chopper.dart';
+import 'package:coffeecard/core/extensions/task_extensions.dart';
import 'package:coffeecard/core/throttler.dart';
import 'package:coffeecard/features/authentication.dart';
import 'package:coffeecard/features/login/data/datasources/account_remote_data_source.dart';
import 'package:coffeecard/generated/api/coffeecard_api.models.swagger.dart'
show LoginDto;
import 'package:fpdart/fpdart.dart';
-import 'package:get_it/get_it.dart';
import 'package:logger/logger.dart';
-class RetryAuthenticator extends chopper.Authenticator {
+class RetryAuthenticator extends Authenticator {
/// Creates a new [RetryAuthenticator] instance.
///
/// This instance is not ready to be used. Call [initialize] before using it.
- RetryAuthenticator.uninitialized({required GetIt serviceLocator})
- : _repository = serviceLocator(),
- _cubit = serviceLocator(),
- _logger = serviceLocator();
+ RetryAuthenticator.uninitialized({
+ required AuthenticationRepository repository,
+ required AuthenticationCubit cubit,
+ required Logger logger,
+ }) : _repository = repository,
+ _cubit = cubit,
+ _logger = logger;
final AuthenticationRepository _repository;
final AuthenticationCubit _cubit;
@@ -26,30 +29,30 @@ class RetryAuthenticator extends chopper.Authenticator {
// Will be set by [initialize].
late final AccountRemoteDataSource _accountRemoteDataSource;
- final _throttler = Throttler