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 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 _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(); } }