HOME · GALAXY · 행성·성운 상세 · 팬덤 라운지 · 이클립스 화면
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../../core/data/backend.dart';
|
||||
import 'galaxy_data.dart';
|
||||
import '../../core/i18n/app_locale.dart';
|
||||
|
||||
/// HOME과 Galaxy의 동일한 기여 거래: 연타 합산, 확정 결과, 동일 키 재확인.
|
||||
class ZapController extends ChangeNotifier {
|
||||
ZapController(this.repository, {required this.onFailure});
|
||||
final GalaxyRepository repository;
|
||||
final ValueChanged<String> onFailure;
|
||||
int? balance;
|
||||
int pending = 0;
|
||||
bool sending = false;
|
||||
int touchSerial = 0;
|
||||
ZapResult? result;
|
||||
Timer? _burst;
|
||||
bool _disposed = false;
|
||||
({String fandomId, int count, String key})? _batch;
|
||||
bool get needsRetry => _batch != null && !sending;
|
||||
|
||||
void tap(String fandomId) {
|
||||
if (_disposed || sending || _batch != null || fandomId.isEmpty) return;
|
||||
if (pending == 0) HapticFeedback.lightImpact();
|
||||
pending++;
|
||||
touchSerial++;
|
||||
notifyListeners();
|
||||
_burst ??= Timer(const Duration(milliseconds: 450), () => _flush(fandomId));
|
||||
}
|
||||
|
||||
void retry() {
|
||||
if (_batch case final batch?) _flush(batch.fandomId);
|
||||
}
|
||||
|
||||
Future<void> _flush(String fandomId) async {
|
||||
if (_disposed || sending) return;
|
||||
final batch =
|
||||
_batch ??
|
||||
(
|
||||
fandomId: fandomId,
|
||||
count: pending,
|
||||
key: 'z${DateTime.now().microsecondsSinceEpoch}',
|
||||
);
|
||||
if (batch.count == 0) return;
|
||||
_burst?.cancel();
|
||||
_burst = null;
|
||||
_batch = batch;
|
||||
sending = true;
|
||||
pending = 0;
|
||||
notifyListeners();
|
||||
try {
|
||||
final receipt = await repository.zap(
|
||||
fandomId: batch.fandomId,
|
||||
count: batch.count,
|
||||
clientSeq: batch.key,
|
||||
);
|
||||
if (_disposed) return;
|
||||
if (receipt.txId.isEmpty) throw StateError('Missing ZAP receipt');
|
||||
if (receipt.balanceAfter >= 0) balance = receipt.balanceAfter;
|
||||
result = receipt;
|
||||
_batch = null;
|
||||
} catch (error) {
|
||||
if (_disposed) return;
|
||||
onFailure(switch (error) {
|
||||
BackendException(code: 'E-POP-01') => appL10n.zapFailNoPop,
|
||||
BackendException(code: 'E-POP-02') => appL10n.zapFailLedger,
|
||||
BackendException(code: 'E-AUTH-03') => appL10n.authLoginRequired,
|
||||
_ => appL10n.zapFailRetry,
|
||||
});
|
||||
} finally {
|
||||
if (!_disposed) {
|
||||
sending = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
_burst?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user