import 'package:flutter/material.dart'; import 'remote_settings.dart'; import 'remote_http.dart'; class RemoteSettingsPage extends StatefulWidget { const RemoteSettingsPage({super.key}); @override State createState() => _RemoteSettingsPageState(); } class _RemoteSettingsPageState extends State { final _form = GlobalKey(); bool _loaded = false; bool _saving = false; bool _enabled = RemoteSettings.defaultEnabled; final _baseUrl = TextEditingController(text: RemoteSettings.defaultBaseUrl); final _indexPath = TextEditingController(text: RemoteSettings.defaultIndexPath); final _email = TextEditingController(); final _password = TextEditingController(); @override void initState() { super.initState(); _load(); } @override void dispose() { _baseUrl.dispose(); _indexPath.dispose(); _email.dispose(); _password.dispose(); super.dispose(); } Future _load() async { try { final s = await RemoteSettings.load(); if (!mounted) return; setState(() { _enabled = s.enabled; _baseUrl.text = s.baseUrl; _indexPath.text = s.indexPath; _email.text = s.email; _password.text = s.password; _loaded = true; }); } catch (e) { // Fail-open: apri comunque con default/blank e notifica _showSnack('Impossibile leggere le impostazioni sicure: $e'); if (!mounted) return; setState(() { _enabled = RemoteSettings.defaultEnabled; _baseUrl.text = RemoteSettings.defaultBaseUrl; _indexPath.text = RemoteSettings.defaultIndexPath; _email.text = ''; _password.text = ''; _loaded = true; }); } } String? _validateBaseUrl(String? v) { final s = (v ?? '').trim(); if (s.isEmpty) return 'Obbligatorio'; final uri = Uri.tryParse(s); if (uri == null || !(uri.hasScheme && (uri.scheme == 'http' || uri.scheme == 'https'))) { return 'URL non valida (deve iniziare con http/https)'; } // opzionale: blocca spazi/controlli interni if (RegExp(r'[\u200B-\u200F\u202A-\u202E\u2060-\u2064\uFEFF]').hasMatch(s)) { return 'URL contiene caratteri non validi (invisibili)'; } return null; } String? _validateIndex(String? v) { final s = (v ?? '').trim(); if (s.isEmpty) return 'Obbligatorio'; return null; } Future _save() async { if (!(_form.currentState?.validate() ?? false)) return; setState(() => _saving = true); try { final s = RemoteSettings( enabled: _enabled, baseUrl: _baseUrl.text.trim(), indexPath: _indexPath.text.trim(), email: _email.text.trim(), password: _password.text, ); await s.save(); // ✅ forza Aves a usare SUBITO base URL & token aggiornati await RemoteHttp.refreshFromSettings(); await RemoteHttp.warmUp(); // non bloccante: utile per loggare stato token/base if (!mounted) return; _showSnack('Impostazioni remote salvate'); Navigator.of(context).maybePop(); } catch (e) { if (!mounted) return; _showSnack('Salvataggio fallito: $e'); } finally { if (mounted) setState(() => _saving = false); } } void _showSnack(String msg) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( behavior: SnackBarBehavior.fixed, // evita "floating off screen" content: Text(msg), duration: const Duration(seconds: 3), ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar(title: const Text('Remote Settings')), body: !_loaded ? const Center(child: CircularProgressIndicator()) : AbsorbPointer( absorbing: _saving, child: Form( key: _form, child: ListView( padding: const EdgeInsets.all(16), children: [ SwitchListTile( title: const Text('Abilita sync remoto'), value: _enabled, onChanged: (v) => setState(() => _enabled = v), ), const SizedBox(height: 8), TextFormField( controller: _baseUrl, decoration: const InputDecoration( labelText: 'Base URL (es. https://server.tld)', hintText: 'https://example.org', ), keyboardType: TextInputType.url, autovalidateMode: AutovalidateMode.onUserInteraction, validator: _validateBaseUrl, ), const SizedBox(height: 12), TextFormField( controller: _indexPath, decoration: const InputDecoration( labelText: 'Index path (es. photos/)', hintText: 'photos/', ), autovalidateMode: AutovalidateMode.onUserInteraction, validator: _validateIndex, ), const SizedBox(height: 12), TextFormField( controller: _email, decoration: const InputDecoration( labelText: 'User/Email', hintText: 'utente@example.org', ), keyboardType: TextInputType.emailAddress, ), const SizedBox(height: 12), TextFormField( controller: _password, obscureText: true, decoration: const InputDecoration( labelText: 'Password', ), ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _saving ? null : _save, icon: _saving ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( theme.colorScheme.onPrimary, ), ), ) : const Icon(Icons.save), label: Text(_saving ? 'Salvataggio in corso...' : 'Salva'), ), ), ], ), ), ), ); } }