Files
rmtPocketWatcher/flutter_app/lib/widgets/alerts_panel.dart
2025-12-14 21:53:46 -05:00

345 lines
14 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import '../providers/price_provider.dart';
class AlertsPanel extends StatefulWidget {
const AlertsPanel({super.key});
@override
State<AlertsPanel> createState() => _AlertsPanelState();
}
class _AlertsPanelState extends State<AlertsPanel> {
final _auecController = TextEditingController();
final _priceController = TextEditingController();
String _selectedPreset = '1T';
double _auecAmount = 1000000000000; // 1 trillion AUEC
bool _showCustomInput = false;
// Preset AUEC amounts
static const Map<String, double> _presetAmounts = {
'10T': 10000000000000,
'5T': 5000000000000,
'1T': 1000000000000,
'750B': 750000000000,
'500B': 500000000000,
'250B': 250000000000,
'Other': 0, // Special case for custom input
};
@override
void dispose() {
_auecController.dispose();
_priceController.dispose();
super.dispose();
}
void _updateAmount(String preset) {
setState(() {
_selectedPreset = preset;
if (preset == 'Other') {
_showCustomInput = true;
_auecController.text = _auecAmount.toStringAsFixed(0);
} else {
_showCustomInput = false;
_auecAmount = _presetAmounts[preset]!;
}
});
}
void _setCustomAmount() {
final amount = double.tryParse(_auecController.text);
if (amount != null && amount > 0) {
setState(() {
_auecAmount = amount;
});
}
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF1A1F3A),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: const Color(0xFF50E3C2), width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Price Alerts',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'monospace',
),
),
const SizedBox(height: 15),
Row(
children: [
// AUEC amount selector
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// Dropdown for preset amounts
Container(
height: 40,
decoration: BoxDecoration(
color: const Color(0xFF2A2F4A),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: const Color(0xFF50E3C2), width: 1),
),
child: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _selectedPreset,
dropdownColor: const Color(0xFF2A2F4A),
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontFamily: 'monospace',
),
alignment: AlignmentDirectional.center,
items: _presetAmounts.keys.map((String preset) {
return DropdownMenuItem<String>(
value: preset,
alignment: AlignmentDirectional.center,
child: Center(
child: Text(
preset,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontFamily: 'monospace',
),
textAlign: TextAlign.center,
),
),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
_updateAmount(newValue);
}
},
),
),
),
),
if (_showCustomInput) ...[
const SizedBox(width: 8),
Container(
width: 120,
height: 40,
decoration: BoxDecoration(
color: const Color(0xFF2A2F4A),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: const Color(0xFF50E3C2), width: 1),
),
child: TextField(
controller: _auecController,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontFamily: 'monospace',
),
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
hintText: 'Enter amount',
hintStyle: TextStyle(
color: Color(0xFF888888),
fontSize: 12,
),
),
textAlign: TextAlign.center,
textAlignVertical: TextAlignVertical.center,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
onSubmitted: (_) => _setCustomAmount(),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _setCustomAmount,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF50E3C2),
foregroundColor: const Color(0xFF0A0E27),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
minimumSize: const Size(0, 40),
),
child: const Text(
'Set',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
],
),
],
),
const SizedBox(width: 10),
Expanded(
child: TextField(
controller: _priceController,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
hintText: 'Max USD Price',
hintStyle: const TextStyle(color: Color(0xFF888888)),
filled: true,
fillColor: const Color(0xFF2A2F4A),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: Color(0xFF50E3C2)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: Color(0xFF50E3C2)),
),
),
keyboardType: const TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*')),
],
),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async {
final price = double.tryParse(_priceController.text);
if (_auecAmount > 0 && price != null && price > 0) {
await context.read<PriceProvider>().addAlert(_auecAmount, price);
_priceController.clear();
// Reset to default preset
setState(() {
_selectedPreset = '1T';
_auecAmount = 1000000000000;
_showCustomInput = false;
_auecController.clear();
});
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF50E3C2),
foregroundColor: const Color(0xFF0A0E27),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
),
child: const Text(
'Add Alert',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
const SizedBox(height: 15),
Consumer<PriceProvider>(
builder: (context, provider, child) {
if (provider.alerts.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(20),
child: Text(
'No alerts set. Add an alert to get notified when prices meet your criteria.',
style: TextStyle(color: Color(0xFF888888)),
textAlign: TextAlign.center,
),
),
);
}
return Column(
children: provider.alerts.map((alert) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF2A2F4A),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: alert.enabled
? const Color(0xFF50E3C2)
: const Color(0xFF888888),
width: 2,
),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${NumberFormat('#,###').format(alert.auecAmount)} AUEC for \$${alert.maxPrice.toStringAsFixed(2)}',
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
'\$${(alert.maxPrice / (alert.auecAmount / 1000000)).toStringAsFixed(9)} per 1M AUEC',
style: const TextStyle(
color: Color(0xFF888888),
fontSize: 12,
),
),
],
),
),
ElevatedButton(
onPressed: () => provider.toggleAlert(alert.id),
style: ElevatedButton.styleFrom(
backgroundColor: alert.enabled
? const Color(0xFF50E3C2)
: const Color(0xFF888888),
foregroundColor: const Color(0xFF0A0E27),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
),
child: Text(
alert.enabled ? 'Enabled' : 'Disabled',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () => provider.deleteAlert(alert.id),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFFF6B9D),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
),
child: const Text(
'Delete',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}).toList(),
);
},
),
],
),
);
}
}