fix(wallet): 丢弃切换账号后返回的旧余额响应
This commit is contained in:
1 parent
19196ceb8e
commit
501bc66282
2 files changed
+98
-5
No files matched your search
@@ -21,23 +21,29 @@ class WalletController extends ChangeNotifier {
|
|||||||
String? unlockError;
|
String? unlockError;
|
||||||
String? unlockingEpisodeId;
|
String? unlockingEpisodeId;
|
||||||
bool _disposed = false;
|
bool _disposed = false;
|
||||||
|
int _requestSeq = 0;
|
||||||
|
|
||||||
int get balance => snapshot?.wallet.coinBalance ?? 0;
|
int get balance => snapshot?.wallet.coinBalance ?? 0;
|
||||||
List<CoinLedgerEntry> get ledger => snapshot?.ledger ?? const [];
|
List<CoinLedgerEntry> get ledger => snapshot?.ledger ?? const [];
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
|
if (_disposed) return;
|
||||||
|
final request = ++_requestSeq;
|
||||||
loading = true;
|
loading = true;
|
||||||
error = null;
|
error = null;
|
||||||
_notify();
|
_notify();
|
||||||
try {
|
try {
|
||||||
snapshot = await _repository.fetchWallet();
|
final result = await _repository.fetchWallet();
|
||||||
|
if (_isCurrent(request)) snapshot = result;
|
||||||
} on ApiException catch (exception) {
|
} on ApiException catch (exception) {
|
||||||
error = exception.message;
|
if (_isCurrent(request)) error = exception.message;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
error = 'walletLoadFailed';
|
if (_isCurrent(request)) error = 'walletLoadFailed';
|
||||||
} finally {
|
} finally {
|
||||||
loading = false;
|
if (_isCurrent(request)) {
|
||||||
_notify();
|
loading = false;
|
||||||
|
_notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +81,7 @@ class WalletController extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
|
_requestSeq += 1;
|
||||||
snapshot = null;
|
snapshot = null;
|
||||||
loading = false;
|
loading = false;
|
||||||
unlocking = false;
|
unlocking = false;
|
||||||
@@ -88,6 +95,8 @@ class WalletController extends ChangeNotifier {
|
|||||||
if (!_disposed) notifyListeners();
|
if (!_disposed) notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isCurrent(int request) => !_disposed && request == _requestSeq;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
// 验证登出、切换请求及销毁后不会回写旧钱包。
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:fruitshorts/core/data/models/wallet.dart';
|
||||||
|
import 'package:fruitshorts/core/data/repositories/commerce_repository.dart';
|
||||||
|
import 'package:fruitshorts/core/data/services/wallet_controller.dart';
|
||||||
|
|
||||||
|
const _oldBalance = 320;
|
||||||
|
const _newBalance = 50;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('should_discard_inflight_balance_when_identity_is_reset', () async {
|
||||||
|
final repository = _PendingCommerce();
|
||||||
|
final wallet = WalletController(repository);
|
||||||
|
final pending = wallet.load();
|
||||||
|
|
||||||
|
wallet.reset();
|
||||||
|
repository.requests.single.complete(_snapshot(_oldBalance));
|
||||||
|
await pending;
|
||||||
|
|
||||||
|
expect(wallet.snapshot, isNull);
|
||||||
|
expect(wallet.loading, isFalse);
|
||||||
|
wallet.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should_keep_new_balance_when_an_older_request_finishes_last', () async {
|
||||||
|
final repository = _PendingCommerce();
|
||||||
|
final wallet = WalletController(repository);
|
||||||
|
final oldRequest = wallet.load();
|
||||||
|
wallet.reset();
|
||||||
|
final newRequest = wallet.load();
|
||||||
|
|
||||||
|
repository.requests.last.complete(_snapshot(_newBalance));
|
||||||
|
await newRequest;
|
||||||
|
repository.requests.first.complete(_snapshot(_oldBalance));
|
||||||
|
await oldRequest;
|
||||||
|
|
||||||
|
expect(wallet.balance, _newBalance);
|
||||||
|
expect(wallet.loading, isFalse);
|
||||||
|
wallet.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should_discard_inflight_error_when_wallet_is_reset', () async {
|
||||||
|
final repository = _PendingCommerce();
|
||||||
|
final wallet = WalletController(repository);
|
||||||
|
final pending = wallet.load();
|
||||||
|
|
||||||
|
wallet.reset();
|
||||||
|
repository.requests.single.completeError(StateError('old request'));
|
||||||
|
await pending;
|
||||||
|
|
||||||
|
expect(wallet.error, isNull);
|
||||||
|
expect(wallet.snapshot, isNull);
|
||||||
|
wallet.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
WalletSnapshot _snapshot(int balance) => WalletSnapshot(
|
||||||
|
wallet: CoinWallet(
|
||||||
|
coinBalance: balance,
|
||||||
|
totalRechargedCoin: balance,
|
||||||
|
totalConsumedCoin: 0,
|
||||||
|
totalRefundedCoin: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
class _PendingCommerce implements CommerceRepository {
|
||||||
|
final requests = <Completer<WalletSnapshot>>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WalletSnapshot> fetchWallet() {
|
||||||
|
final request = Completer<WalletSnapshot>();
|
||||||
|
requests.add(request);
|
||||||
|
return request.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<UnlockResult> unlock(String episodeId) async {
|
||||||
|
throw UnimplementedError('not needed by wallet identity tests');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user