151 lines
3.7 KiB
Dart
151 lines
3.7 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:sxchnge_analytics/sxchnge_analytics.dart';
|
|
|
|
import '../models/wallet.dart';
|
|
import '../services/contracts.dart';
|
|
|
|
class WalletProvider extends ChangeNotifier {
|
|
WalletProvider(this.service, this.analytics);
|
|
final WalletService service;
|
|
final AnalyticsService analytics;
|
|
WalletSnapshot? data;
|
|
bool loading = false,
|
|
failed = false,
|
|
profileLoading = false,
|
|
profileFailed = false,
|
|
saving = false,
|
|
saveFailed = false;
|
|
String? preferredLocale;
|
|
bool _disposed = false;
|
|
int _revision = 0;
|
|
Timer? _poll;
|
|
final _settlements = <String, (JourneyRecorder, JourneyOperation)>{};
|
|
CurrencyAccount? account(String? id) {
|
|
for (final a in data?.accounts ?? <CurrencyAccount>[]) {
|
|
if (a.id == id) return a;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
WalletOperation? operation(String id) {
|
|
for (final o in data?.operations ?? <WalletOperation>[]) {
|
|
if (o.id == id) return o;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
bool get hasPending =>
|
|
data?.operations.any((o) => o.status == OperationStatus.pending) ?? false;
|
|
Future<void> refresh() async {
|
|
final rev = ++_revision;
|
|
loading = true;
|
|
failed = false;
|
|
notifyListeners();
|
|
try {
|
|
final result = await service.fetch();
|
|
if (_disposed || rev != _revision) return;
|
|
data = result;
|
|
for (final o in result.operations) {
|
|
if (o.status == OperationStatus.pending) continue;
|
|
final pair = _settlements.remove(o.id);
|
|
if (pair != null) {
|
|
pair.$1.finish(
|
|
pair.$2,
|
|
simulated: service.isSimulated,
|
|
failure: o.status == OperationStatus.failed
|
|
? JourneyFailure.declined
|
|
: null,
|
|
terminal: true,
|
|
);
|
|
pair.$1.dispose();
|
|
}
|
|
}
|
|
} catch (_) {
|
|
if (_disposed || rev != _revision) return;
|
|
failed = true;
|
|
}
|
|
if (_disposed || rev != _revision) return;
|
|
loading = false;
|
|
_poll?.cancel();
|
|
if (hasPending) {
|
|
_poll = Timer(const Duration(seconds: 3), () {
|
|
if (!_disposed) refresh();
|
|
});
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
void track(
|
|
WalletOperation operation,
|
|
JourneyRecorder recorder,
|
|
JourneyKind kind,
|
|
) {
|
|
final step = recorder.begin(
|
|
kind,
|
|
JourneyStep.settlement,
|
|
simulated: service.isSimulated,
|
|
);
|
|
_settlements[operation.id] = (recorder, step);
|
|
final snapshot = data;
|
|
if (snapshot != null) {
|
|
data = WalletSnapshot(
|
|
accounts: snapshot.accounts,
|
|
operations: [
|
|
operation,
|
|
...snapshot.operations.where((o) => o.id != operation.id),
|
|
],
|
|
);
|
|
}
|
|
unawaited(refresh());
|
|
}
|
|
|
|
Future<void> loadProfile() async {
|
|
if (profileLoading) return;
|
|
profileLoading = true;
|
|
profileFailed = false;
|
|
notifyListeners();
|
|
try {
|
|
final result = await service.profile();
|
|
if (_disposed) return;
|
|
preferredLocale = result.locale;
|
|
} catch (_) {
|
|
if (_disposed) return;
|
|
profileFailed = true;
|
|
}
|
|
profileLoading = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<bool> saveLocale(String locale) async {
|
|
if (saving) return false;
|
|
saving = true;
|
|
saveFailed = false;
|
|
notifyListeners();
|
|
try {
|
|
final result = await service.saveLocale(locale);
|
|
if (_disposed) return false;
|
|
preferredLocale = result.locale;
|
|
} catch (_) {
|
|
if (_disposed) return false;
|
|
saveFailed = true;
|
|
}
|
|
saving = false;
|
|
notifyListeners();
|
|
return !saveFailed;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_disposed = true;
|
|
_poll?.cancel();
|
|
for (final p in _settlements.values) {
|
|
p.$1.dispose(abandonActive: false);
|
|
}
|
|
_settlements.clear();
|
|
super.dispose();
|
|
}
|
|
}
|