···
import '../models/coves_session.dart';
import '../providers/vote_provider.dart' show VoteState;
import 'api_exceptions.dart';
8
+
import 'auth_interceptor.dart';
···
/// 4. Handles toggle logic (creating, deleting, or switching vote direction)
/// **Backend Endpoints**:
26
-
/// - POST /xrpc/social.coves.feed.vote.create - Creates, toggles, or switches votes
27
+
/// - POST /xrpc/social.coves.feed.vote.create - Creates, toggles, or switches
Future<CovesSession?> Function()? sessionGetter,
···
Future<void> Function()? signOutHandler,
}) : _sessionGetter = sessionGetter,
35
-
_didGetter = didGetter,
36
-
_tokenRefresher = tokenRefresher,
37
-
_signOutHandler = signOutHandler {
37
+
_didGetter = didGetter {
···
49
-
// Add 401 retry interceptor (same pattern as CovesApiService)
49
+
// Add shared 401 retry interceptor
51
-
InterceptorsWrapper(
52
-
onRequest: (options, handler) async {
53
-
// Fetch fresh token before each request
54
-
final session = await _sessionGetter?.call();
55
-
if (session != null) {
56
-
options.headers['Authorization'] = 'Bearer ${session.token}';
58
-
debugPrint('🔐 VoteService: Adding fresh Authorization header');
63
-
'⚠️ VoteService: Session getter returned null - '
64
-
'making unauthenticated request',
68
-
return handler.next(options);
70
-
onError: (error, handler) async {
71
-
// Handle 401 errors with automatic token refresh
72
-
if (error.response?.statusCode == 401 && _tokenRefresher != null) {
75
-
'🔄 VoteService: 401 detected, attempting token refresh...',
79
-
// Check if we already retried this request (prevent infinite loop)
80
-
if (error.requestOptions.extra['retried'] == true) {
83
-
'⚠️ VoteService: Request already retried after token refresh, '
87
-
// Already retried once, don't retry again
88
-
if (_signOutHandler != null) {
89
-
await _signOutHandler();
91
-
return handler.next(error);
95
-
// Attempt to refresh the token
96
-
final refreshSucceeded = await _tokenRefresher();
98
-
if (refreshSucceeded) {
101
-
'✅ VoteService: Token refresh successful, retrying request',
105
-
// Get the new session
106
-
final newSession = await _sessionGetter?.call();
108
-
if (newSession != null) {
109
-
// Mark this request as retried to prevent infinite loops
110
-
error.requestOptions.extra['retried'] = true;
112
-
// Update the Authorization header with the new token
113
-
error.requestOptions.headers['Authorization'] =
114
-
'Bearer ${newSession.token}';
116
-
// Retry the original request with the new token
118
-
final response = await _dio.fetch(error.requestOptions);
119
-
return handler.resolve(response);
120
-
} on DioException catch (retryError) {
121
-
// If retry failed with 401 and already retried, we already
122
-
// signed out in the retry limit check above, so just pass
123
-
// the error through without signing out again
124
-
if (retryError.response?.statusCode == 401 &&
125
-
retryError.requestOptions.extra['retried'] == true) {
126
-
return handler.next(retryError);
128
-
// For other errors during retry, rethrow to outer catch
134
-
// Refresh failed, sign out the user
137
-
'❌ VoteService: Token refresh failed, signing out user',
140
-
if (_signOutHandler != null) {
141
-
await _signOutHandler();
145
-
debugPrint('❌ VoteService: Error during token refresh: $e');
147
-
// Only sign out if we haven't already (avoid double sign-out)
148
-
// Check if this is a DioException from a retried request
149
-
final isRetriedRequest =
150
-
e is DioException &&
151
-
e.response?.statusCode == 401 &&
152
-
e.requestOptions.extra['retried'] == true;
154
-
if (!isRetriedRequest && _signOutHandler != null) {
155
-
await _signOutHandler();
160
-
// Log the error for debugging
162
-
debugPrint('❌ VoteService API Error: ${error.message}');
163
-
if (error.response != null) {
164
-
debugPrint(' Status: ${error.response?.statusCode}');
165
-
debugPrint(' Data: ${error.response?.data}');
168
-
return handler.next(error);
51
+
createAuthInterceptor(
52
+
sessionGetter: sessionGetter,
53
+
tokenRefresher: tokenRefresher,
54
+
signOutHandler: signOutHandler,
55
+
serviceName: 'VoteService',
final Future<CovesSession?> Function()? _sessionGetter;
final String? Function()? _didGetter;
176
-
final Future<bool> Function()? _tokenRefresher;
177
-
final Future<void> Function()? _signOutHandler;
/// Collection name for vote records
···
statusCode: e.response?.statusCode,
161
+
} on ApiException {
} on Exception catch (e) {
throw ApiException('Failed to create vote: $e');