241 lines
7.8 KiB
Dart
241 lines
7.8 KiB
Dart
// lib/screens/settings.dart
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:environment_monitoring_app/auth_provider.dart';
|
|
|
|
class SettingsScreen extends StatefulWidget {
|
|
const SettingsScreen({super.key});
|
|
|
|
@override
|
|
State<SettingsScreen> createState() => _SettingsScreenState();
|
|
}
|
|
|
|
class _SettingsScreenState extends State<SettingsScreen> {
|
|
bool _isSyncingData = false;
|
|
|
|
Future<void> _manualDataSync() async {
|
|
if (_isSyncingData) return;
|
|
setState(() => _isSyncingData = true);
|
|
|
|
final auth = Provider.of<AuthProvider>(context, listen: false);
|
|
|
|
// 1. Pre-sync checks
|
|
if (!await auth.isConnected()) {
|
|
_showSnackBar('Sync failed: No internet connection.', isError: true);
|
|
setState(() => _isSyncingData = false);
|
|
return;
|
|
}
|
|
|
|
if (auth.isSessionExpired) {
|
|
_showSessionExpiredDialog();
|
|
setState(() => _isSyncingData = false);
|
|
return;
|
|
}
|
|
|
|
// 2. Attempt the sync operation
|
|
try {
|
|
await auth.syncAllData(forceRefresh: true);
|
|
// No need to reload settings here, child screens will load on demand
|
|
if (mounted) {
|
|
_showSnackBar('Data synced successfully.', isError: false);
|
|
}
|
|
} catch (e) {
|
|
// 3. Handle failures
|
|
if (mounted) {
|
|
if (auth.isSessionExpired) {
|
|
_showSessionExpiredDialog();
|
|
} else {
|
|
_showSnackBar('Data sync failed: ${e.toString()}', isError: true);
|
|
}
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() => _isSyncingData = false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _showSessionExpiredDialog() {
|
|
if (!mounted) return;
|
|
showDialog(
|
|
context: context,
|
|
builder: (dialogContext) => AlertDialog(
|
|
title: const Text("Session Expired"),
|
|
content: const Text("Your session has expired and automatic re-login failed. Please log out and log in again to sync data."),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(dialogContext),
|
|
child: const Text("OK"),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
final auth = Provider.of<AuthProvider>(context, listen: false);
|
|
Navigator.pop(dialogContext);
|
|
auth.logout();
|
|
Navigator.pushNamedAndRemoveUntil(context, '/', (route) => false);
|
|
},
|
|
child: const Text("Logout"),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showSnackBar(String message, {bool isError = false}) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: isError ? Theme.of(context).colorScheme.error : Colors.green,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget _buildSectionHeader(BuildContext context, String title) {
|
|
return Padding(
|
|
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 16.0),
|
|
child: Text(
|
|
title,
|
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildNavTile(BuildContext context, {
|
|
required String title,
|
|
required String subtitle,
|
|
required IconData icon,
|
|
required String routeName,
|
|
}) {
|
|
return Card(
|
|
margin: const EdgeInsets.symmetric(vertical: 6.0),
|
|
child: ListTile(
|
|
leading: Icon(icon, size: 32.0, color: Theme.of(context).colorScheme.primary),
|
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
subtitle: Text(subtitle),
|
|
trailing: const Icon(Icons.arrow_forward_ios),
|
|
onTap: () {
|
|
Navigator.pushNamed(context, routeName);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final auth = Provider.of<AuthProvider>(context);
|
|
final lastSync = auth.lastSyncTimestamp;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text("Settings"),
|
|
),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionHeader(context, "Synchronization"),
|
|
Card(
|
|
margin: const EdgeInsets.symmetric(vertical: 6.0),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Text("Last Data Sync:", style: Theme.of(context).textTheme.titleMedium),
|
|
const SizedBox(height: 4),
|
|
Text(lastSync != null ? DateFormat('yyyy-MM-dd HH:mm:ss').format(lastSync.toLocal()) : 'Never', style: Theme.of(context).textTheme.bodyLarge),
|
|
const SizedBox(height: 16),
|
|
ElevatedButton.icon(
|
|
onPressed: _isSyncingData ? null : _manualDataSync,
|
|
icon: _isSyncingData ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) : const Icon(Icons.cloud_sync),
|
|
label: Text(_isSyncingData ? 'Syncing Data...' : 'Sync App Data'),
|
|
style: ElevatedButton.styleFrom(minimumSize: const Size(double.infinity, 50)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
|
|
_buildSectionHeader(context, "App Management"),
|
|
|
|
_buildNavTile(
|
|
context,
|
|
title: "Submission Preferences",
|
|
subtitle: "Manage API & FTP submissions for each module.",
|
|
icon: Icons.send_to_mobile,
|
|
routeName: '/settings/submission-prefs',
|
|
),
|
|
|
|
_buildNavTile(
|
|
context,
|
|
title: "Telegram Alert Settings",
|
|
subtitle: "View Telegram chat IDs for alerts.",
|
|
icon: Icons.telegram,
|
|
routeName: '/settings/telegram-alerts',
|
|
),
|
|
|
|
_buildNavTile(
|
|
context,
|
|
title: "API/FTP Configurations",
|
|
subtitle: "View synced server configurations.",
|
|
icon: Icons.dns,
|
|
routeName: '/settings/api-ftp-configs',
|
|
),
|
|
|
|
_buildNavTile(
|
|
context,
|
|
title: "Parameter Limits",
|
|
subtitle: "View parameter limits for all modules.",
|
|
icon: Icons.science_outlined,
|
|
routeName: '/settings/parameter-limits',
|
|
),
|
|
|
|
_buildNavTile(
|
|
context,
|
|
title: "Air Clients",
|
|
subtitle: "View and search for synced Air clients.",
|
|
icon: Icons.air,
|
|
routeName: '/settings/air-clients',
|
|
),
|
|
|
|
_buildNavTile(
|
|
context,
|
|
title: "Station Info",
|
|
subtitle: "View and search all synced stations.",
|
|
icon: Icons.location_on,
|
|
routeName: '/settings/station-info',
|
|
),
|
|
|
|
const SizedBox(height: 32),
|
|
_buildSectionHeader(context, "Other Information"),
|
|
Card(
|
|
margin: const EdgeInsets.symmetric(vertical: 6.0),
|
|
child: Column(
|
|
children: [
|
|
ListTile(
|
|
leading: const Icon(Icons.info_outline),
|
|
title: const Text('App Version'),
|
|
subtitle: const Text('MMS Version 3.12.01'),
|
|
dense: true,
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.privacy_tip_outlined),
|
|
title: const Text('Privacy Policy'),
|
|
onTap: () {},
|
|
dense: true,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |