import 'package:flutter/material.dart'; import 'relay_management_controller.dart'; import '../../data/nostr/models/nostr_relay.dart'; /// Screen for managing Nostr relays. /// /// Allows users to view, add, remove, test, and toggle relays. class RelayManagementScreen extends StatefulWidget { /// Controller for managing relay state. final RelayManagementController controller; /// Creates a [RelayManagementScreen] instance. const RelayManagementScreen({ super.key, required this.controller, }); @override State createState() => _RelayManagementScreenState(); } class _RelayManagementScreenState extends State { final TextEditingController _urlController = TextEditingController(); @override void dispose() { _urlController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Nostr Relay Management'), ), body: ListenableBuilder( listenable: widget.controller, builder: (context, child) { return Column( children: [ // Error message if (widget.controller.error != null) Container( width: double.infinity, padding: const EdgeInsets.all(16), color: Colors.red.shade100, child: Row( children: [ Icon(Icons.error, color: Colors.red.shade700), const SizedBox(width: 8), Expanded( child: Text( widget.controller.error!, style: TextStyle(color: Colors.red.shade700), ), ), IconButton( icon: const Icon(Icons.close), onPressed: widget.controller.clearError, color: Colors.red.shade700, ), ], ), ), // Top action buttons Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Test All and Toggle All buttons Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: widget.controller.isCheckingHealth ? null : widget.controller.checkRelayHealth, icon: widget.controller.isCheckingHealth ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.network_check), label: const Text('Test All'), ), ), const SizedBox(width: 8), Expanded( child: ElevatedButton.icon( onPressed: widget.controller.relays.isEmpty ? null : () async { await widget.controller.toggleAllRelays(); }, icon: const Icon(Icons.power_settings_new), label: Text( widget.controller.relays.isNotEmpty && widget.controller.relays.every((r) => r.isEnabled) ? 'Turn All Off' : 'Turn All On', ), ), ), ], ), const SizedBox(height: 16), // Add relay input Row( children: [ Expanded( child: TextField( controller: _urlController, decoration: InputDecoration( labelText: 'Relay URL', hintText: 'wss://relay.example.com', border: const OutlineInputBorder(), ), keyboardType: TextInputType.url, ), ), const SizedBox(width: 8), ElevatedButton.icon( onPressed: () async { final url = _urlController.text.trim(); if (url.isNotEmpty) { final success = await widget.controller.addRelay(url); if (mounted) { if (success) { _urlController.clear(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Relay added and connected successfully'), backgroundColor: Colors.green, duration: Duration(seconds: 2), ), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( widget.controller.error ?? 'Failed to connect to relay', ), backgroundColor: Colors.orange, duration: const Duration(seconds: 3), ), ); } } } }, icon: const Icon(Icons.add), label: const Text('Add'), ), ], ), ], ), ), const Divider(), // Relay list Expanded( child: widget.controller.relays.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.cloud_off, size: 64, color: Colors.grey.shade400, ), const SizedBox(height: 16), Text( 'No relays configured', style: Theme.of(context).textTheme.titleLarge?.copyWith( color: Colors.grey, ), ), const SizedBox(height: 8), Text( 'Add a relay to get started', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey, ), ), ], ), ) : ListView.builder( itemCount: widget.controller.relays.length, itemBuilder: (context, index) { final relay = widget.controller.relays[index]; return _RelayListItem( relay: relay, onToggle: () async { await widget.controller.toggleRelay(relay.url); }, onRemove: () { widget.controller.removeRelay(relay.url); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Relay ${relay.url} removed'), duration: const Duration(seconds: 2), ), ); }, ); }, ), ), ], ); }, ), ); } } /// Widget for displaying a single relay in the list. class _RelayListItem extends StatelessWidget { /// The relay to display. final NostrRelay relay; /// Callback when toggle is pressed. final VoidCallback onToggle; /// Callback when remove is pressed. final VoidCallback onRemove; const _RelayListItem({ required this.relay, required this.onToggle, required this.onRemove, }); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Relay URL and status Row( children: [ // Status indicator // Enabled means connected - if it's enabled but not connected, it should be disabled Container( width: 12, height: 12, decoration: BoxDecoration( shape: BoxShape.circle, color: relay.isConnected && relay.isEnabled ? Colors.green : Colors.grey, ), ), const SizedBox(width: 8), Expanded( child: Text( relay.url, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, ), ), ), ], ), const SizedBox(height: 8), // Status text // Enabled means connected - if it's enabled but not connected, it should be disabled Text( relay.isConnected && relay.isEnabled ? 'Connected' : 'Disabled', style: TextStyle( fontSize: 12, color: relay.isConnected && relay.isEnabled ? Colors.green : Colors.grey, ), ), const SizedBox(height: 12), // Action buttons Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // Toggle switch Row( children: [ Text( relay.isEnabled ? 'On' : 'Off', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), const SizedBox(width: 4), Switch( value: relay.isEnabled, onChanged: (_) => onToggle(), ), ], ), const SizedBox(width: 8), // Remove button IconButton( icon: const Icon(Icons.delete, size: 20), color: Colors.red, tooltip: 'Remove', onPressed: onRemove, ), ], ), ], ), ), ); } }