feat: Implement server data deletion functionality with confirmation dialog in SettingsScreen

This commit is contained in:
2026-05-20 17:33:22 +02:00
parent 3ff4efb738
commit 2ef9cf1dbb
2 changed files with 138 additions and 16 deletions
+29
View File
@@ -217,6 +217,35 @@ class AuthApi {
} }
} }
Future<Map<String, dynamic>> deleteAllServerData({String? endpoint}) async {
final String? token = await accessToken;
if (token == null) {
return {'error': true, 'message': 'No access token available'};
}
final String base = endpoint ?? await ApiConfig.getEndpoint();
final Uri url = Uri.parse('$base/auth/delete-all-data');
final http.Response res = await http.post(
url,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
);
if (res.statusCode >= 200 && res.statusCode < 300) {
return {'error': false, 'status': res.statusCode, 'body': res.body};
}
try {
final dynamic decoded = jsonDecode(res.body);
return {'error': true, 'status': res.statusCode, 'body': decoded};
} catch (_) {
return {'error': true, 'status': res.statusCode, 'body': res.body};
}
}
List<int> _randomBytes(int length) { List<int> _randomBytes(int length) {
final Random random = Random.secure(); final Random random = Random.secure();
return List<int>.generate(length, (_) => random.nextInt(256)); return List<int>.generate(length, (_) => random.nextInt(256));
+108 -15
View File
@@ -26,6 +26,7 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
bool _isBusy = false; bool _isBusy = false;
bool _isSyncing = false; bool _isSyncing = false;
bool _isServerDeleting = false;
bool _isThemeSaving = false; bool _isThemeSaving = false;
final TextEditingController _endpointController = TextEditingController(); final TextEditingController _endpointController = TextEditingController();
final TextEditingController _encryptionKeyController = TextEditingController(); final TextEditingController _encryptionKeyController = TextEditingController();
@@ -83,6 +84,64 @@ class _SettingsScreenState extends State<SettingsScreen> {
} }
} }
Future<void> _confirmAndDeleteServerData() async {
final bool? confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Borrar toda la info del servidor'),
content: const Text(
'¿Estás seguro? Esta acción eliminará toda la información almacenada en el servidor y no se puede deshacer.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancelar'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text(
'Borrar',
style: TextStyle(color: Colors.red),
),
),
],
),
);
if (confirmed != true) return;
setState(() {
_isServerDeleting = true;
});
try {
final Map<String, dynamic> response =
await AuthApi.instance.deleteAllServerData();
if (response['error'] == true) {
throw Exception(response['body'] ?? response['message'] ?? 'Error desconocido');
}
await AuthApi.instance.clearTokens();
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Toda la información del servidor ha sido eliminada.')),
);
} catch (error) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error al borrar la info del servidor: $error')),
);
} finally {
if (!mounted) return;
setState(() {
_isServerDeleting = false;
});
}
}
Future<void> _forceSync() async { Future<void> _forceSync() async {
if (_isBusy || _isSyncing) { if (_isBusy || _isSyncing) {
return; return;
@@ -219,6 +278,30 @@ class _SettingsScreenState extends State<SettingsScreen> {
}); });
} }
Widget _buildDestructiveButton({
required String label,
required VoidCallback? onPressed,
required bool isLoading,
required IconData icon,
}) {
return ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent,
foregroundColor: Colors.white,
textStyle: const TextStyle(fontWeight: FontWeight.w600),
),
onPressed: onPressed,
icon: isLoading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Icon(icon),
label: Text(label),
);
}
Widget _buildThemeColorButton(Color color) { Widget _buildThemeColorButton(Color color) {
final bool isSelected = _selectedSeedColor.value == color.value; final bool isSelected = _selectedSeedColor.value == color.value;
final Color foregroundColor = final Color foregroundColor =
@@ -382,21 +465,29 @@ class _SettingsScreenState extends State<SettingsScreen> {
const Expanded( const Expanded(
child: Text('Borrar datos locales:'), child: Text('Borrar datos locales:'),
), ),
ElevatedButton.icon( _buildDestructiveButton(
style: ElevatedButton.styleFrom( label: 'Borrar',
backgroundColor: Colors.redAccent, onPressed: (_isBusy || _isServerDeleting)
foregroundColor: Colors.white, ? null
textStyle: const TextStyle(fontWeight: FontWeight.w600), : _confirmAndDeleteAll,
isLoading: _isBusy,
icon: Icons.delete_forever,
), ),
onPressed: _isBusy ? null : _confirmAndDeleteAll, ],
icon: _isBusy ),
? const SizedBox( const SizedBox(height: 16),
width: 16, Row(
height: 16, children: [
child: CircularProgressIndicator(strokeWidth: 2), const Expanded(
) child: Text('Borrar info del servidor:'),
: const Icon(Icons.delete_forever), ),
label: const Text('Borrar'), _buildDestructiveButton(
label: 'Borrar',
onPressed: (_isBusy || _isSyncing || _isServerDeleting)
? null
: _confirmAndDeleteServerData,
isLoading: _isServerDeleting,
icon: Icons.cloud_off,
), ),
], ],
), ),
@@ -407,7 +498,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Text('Forzar sincronizacion total:'), child: Text('Forzar sincronizacion total:'),
), ),
ElevatedButton.icon( ElevatedButton.icon(
onPressed: (_isBusy || _isSyncing) ? null : _forceSync, onPressed: (_isBusy || _isSyncing || _isServerDeleting)
? null
: _forceSync,
icon: _isSyncing icon: _isSyncing
? const SizedBox( ? const SizedBox(
width: 16, width: 16,