177 lines
4.8 KiB
Dart
177 lines
4.8 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/foundation.dart';
|
|
import '../models/price_data.dart';
|
|
import '../services/websocket_service.dart';
|
|
import '../services/api_service.dart';
|
|
import '../services/storage_service.dart';
|
|
import '../services/notification_service.dart';
|
|
|
|
class PriceProvider with ChangeNotifier {
|
|
final WebSocketService _wsService = WebSocketService();
|
|
final ApiService _apiService = ApiService();
|
|
final StorageService _storageService = StorageService();
|
|
final NotificationService _notificationService = NotificationService();
|
|
|
|
LatestPrice? _latestPrice;
|
|
HistoryData? _historyData;
|
|
String _connectionStatus = 'Disconnected';
|
|
List<PriceAlert> _alerts = [];
|
|
double? _customAuecAmount;
|
|
String _selectedRange = '7d';
|
|
bool _isLoading = false;
|
|
bool _isHistoryLoading = false;
|
|
|
|
LatestPrice? get latestPrice => _latestPrice;
|
|
HistoryData? get historyData => _historyData;
|
|
String get connectionStatus => _connectionStatus;
|
|
List<PriceAlert> get alerts => _alerts;
|
|
double? get customAuecAmount => _customAuecAmount;
|
|
String get selectedRange => _selectedRange;
|
|
bool get isLoading => _isLoading;
|
|
bool get isHistoryLoading => _isHistoryLoading;
|
|
|
|
PriceProvider() {
|
|
_initialize();
|
|
}
|
|
|
|
Timer? _pollTimer;
|
|
|
|
void _initialize() {
|
|
// Load saved data
|
|
_loadAlerts();
|
|
_loadCustomAuecAmount();
|
|
|
|
// Connect WebSocket (currently disabled)
|
|
_wsService.connect();
|
|
|
|
// Listen to WebSocket streams
|
|
_wsService.latestPriceStream.listen((price) {
|
|
_latestPrice = price;
|
|
_checkAlerts(price);
|
|
notifyListeners();
|
|
});
|
|
|
|
_wsService.connectionStatusStream.listen((status) {
|
|
_connectionStatus = status;
|
|
notifyListeners();
|
|
});
|
|
|
|
// Fetch initial data
|
|
fetchInitialData();
|
|
|
|
// Start polling for updates every 5 minutes as backup to WebSocket
|
|
_startPolling();
|
|
}
|
|
|
|
void _startPolling() {
|
|
_pollTimer = Timer.periodic(const Duration(minutes: 5), (timer) {
|
|
fetchInitialData();
|
|
});
|
|
}
|
|
|
|
Future<void> fetchInitialData() async {
|
|
final latest = await _apiService.fetchLatestPrice();
|
|
if (latest != null) {
|
|
_latestPrice = latest;
|
|
notifyListeners();
|
|
}
|
|
await fetchHistory(_selectedRange);
|
|
}
|
|
|
|
Future<void> fetchHistory(String range) async {
|
|
_isHistoryLoading = true;
|
|
_selectedRange = range;
|
|
notifyListeners();
|
|
|
|
try {
|
|
final history = await _apiService.fetchHistory(range);
|
|
if (history != null) {
|
|
_historyData = history;
|
|
}
|
|
_wsService.requestHistory(range);
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print('Error fetching history: $e');
|
|
}
|
|
} finally {
|
|
_isHistoryLoading = false;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
Future<void> _loadAlerts() async {
|
|
_alerts = await _storageService.getAlerts();
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> _loadCustomAuecAmount() async {
|
|
_customAuecAmount = await _storageService.getCustomAuecAmount();
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> addAlert(double auecAmount, double maxPrice) async {
|
|
final alert = PriceAlert(
|
|
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
|
auecAmount: auecAmount,
|
|
maxPrice: maxPrice,
|
|
enabled: true,
|
|
);
|
|
await _storageService.addAlert(alert);
|
|
await _loadAlerts();
|
|
}
|
|
|
|
Future<void> toggleAlert(String id) async {
|
|
final alert = _alerts.firstWhere((a) => a.id == id);
|
|
final updated = alert.copyWith(enabled: !alert.enabled);
|
|
await _storageService.updateAlert(updated);
|
|
await _loadAlerts();
|
|
}
|
|
|
|
Future<void> deleteAlert(String id) async {
|
|
await _storageService.deleteAlert(id);
|
|
await _loadAlerts();
|
|
}
|
|
|
|
Future<void> setCustomAuecAmount(double amount) async {
|
|
await _storageService.setCustomAuecAmount(amount);
|
|
_customAuecAmount = amount;
|
|
notifyListeners();
|
|
}
|
|
|
|
void _checkAlerts(LatestPrice price) {
|
|
for (final alert in _alerts) {
|
|
if (!alert.enabled) continue;
|
|
|
|
final matchingSeller = price.allPrices.firstWhere(
|
|
(p) {
|
|
final totalPrice = (alert.auecAmount / 1000000) * p.pricePerMillion;
|
|
return totalPrice <= alert.maxPrice;
|
|
},
|
|
orElse: () => price.allPrices.first,
|
|
);
|
|
|
|
final totalPrice = (alert.auecAmount / 1000000) * matchingSeller.pricePerMillion;
|
|
if (totalPrice <= alert.maxPrice) {
|
|
// Trigger notification
|
|
_notificationService.showPriceAlert(
|
|
title: 'Price Alert Triggered!',
|
|
body: '${(alert.auecAmount / 1000000000).toStringAsFixed(1)}B AUEC available for \$${totalPrice.toStringAsFixed(2)} from ${matchingSeller.sellerName}',
|
|
auecAmount: alert.auecAmount,
|
|
price: totalPrice,
|
|
seller: matchingSeller.sellerName,
|
|
);
|
|
|
|
// Disable alert
|
|
toggleAlert(alert.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_pollTimer?.cancel();
|
|
_wsService.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|