fix(mining-app): remove AuthEventBus to fix Riverpod state race condition

The AuthEventBus was causing "Cannot use ref functions after the
dependency of a provider changed" error when 401 responses triggered
logout during provider rebuilds.

Now 401 handling is done through normal exception flow in splash page
and route guards respond to isLoggedInProvider state changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-12 09:44:53 -08:00
parent add405aa65
commit fe332fdb3f
3 changed files with 2 additions and 42 deletions

View File

@ -756,7 +756,8 @@
"Bash(set DATABASE_URL=postgresql://user:pass@localhost:5432/db)",
"Bash(cmd /c \"set DATABASE_URL=postgresql://user:pass@localhost:5432/db && npx prisma migrate dev --name add_nickname_to_synced_legacy_users --create-only\")",
"Bash(dir \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\frontend\")",
"Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(mining-app\\): fix login bugs and connect contribution page to real API\n\nLogin fixes:\n- Add AuthEventBus for global 401 error handling with auto-logout\n- Add route guards with GoRouter redirect to protect authenticated routes\n- Remove setMockUser\\(\\) security vulnerability and legacy login\\(\\) dead code\n- Remove unused AuthInterceptor class\n\nContribution page:\n- Add ContributionRecord entity and model for records API\n- Connect contribution details card to GET /accounts/{id}/records endpoint\n- Display real team stats \\(direct referrals, unlocked levels/tiers\\)\n- Calculate expiration countdown from actual record data\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
"Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(mining-app\\): fix login bugs and connect contribution page to real API\n\nLogin fixes:\n- Add AuthEventBus for global 401 error handling with auto-logout\n- Add route guards with GoRouter redirect to protect authenticated routes\n- Remove setMockUser\\(\\) security vulnerability and legacy login\\(\\) dead code\n- Remove unused AuthInterceptor class\n\nContribution page:\n- Add ContributionRecord entity and model for records API\n- Connect contribution details card to GET /accounts/{id}/records endpoint\n- Display real team stats \\(direct referrals, unlocked levels/tiers\\)\n- Calculate expiration countdown from actual record data\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
"Bash(dependency of a provider changed\" error when 401 responses triggered\nlogout during provider rebuilds.\n\nNow 401 handling is done through normal exception flow in splash page\nand route guards respond to isLoggedInProvider state changes.\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
],
"deny": [],
"ask": []

View File

@ -1,30 +1,8 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../constants/app_constants.dart';
import '../error/exceptions.dart';
/// 401
class AuthEventBus {
static final AuthEventBus _instance = AuthEventBus._internal();
factory AuthEventBus() => _instance;
AuthEventBus._internal();
final _unauthorizedController = StreamController<void>.broadcast();
///
Stream<void> get onUnauthorized => _unauthorizedController.stream;
///
void emitUnauthorized() {
_unauthorizedController.add(null);
}
void dispose() {
_unauthorizedController.close();
}
}
class ApiClient {
final Dio dio;
@ -129,8 +107,6 @@ class ApiClient {
case DioExceptionType.badResponse:
final statusCode = e.response?.statusCode;
if (statusCode == 401) {
//
AuthEventBus().emitUnauthorized();
return UnauthorizedException();
}
final message = e.response?.data?['error']?['message']?[0] ?? '服务器错误';

View File

@ -1,4 +1,3 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -14,7 +13,6 @@ import '../../presentation/pages/asset/asset_page.dart';
import '../../presentation/pages/profile/profile_page.dart';
import '../../presentation/widgets/main_shell.dart';
import '../../presentation/providers/user_providers.dart';
import '../network/api_client.dart';
import 'routes.dart';
/// 访
@ -28,15 +26,6 @@ const _publicRoutes = {
///
class AuthNotifier extends ChangeNotifier {
bool _isLoggedIn = false;
StreamSubscription<void>? _unauthorizedSubscription;
AuthNotifier() {
// 401
_unauthorizedSubscription = AuthEventBus().onUnauthorized.listen((_) {
_isLoggedIn = false;
notifyListeners();
});
}
bool get isLoggedIn => _isLoggedIn;
@ -46,12 +35,6 @@ class AuthNotifier extends ChangeNotifier {
notifyListeners();
}
}
@override
void dispose() {
_unauthorizedSubscription?.cancel();
super.dispose();
}
}
final authNotifierProvider = ChangeNotifierProvider<AuthNotifier>((ref) {