This repository has been archived on 2026-08-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
melo-app/lib/widgets/cloud_einstellungen.dart
T
Hermes (Server) b3aedc53f9 Feature: Cloud-Daten loeschen in Einstellungen (3s-Halten)
- Einstellungen > Cloud-Daten loeschen: 'Nur Musik' oder 'Komplett vom Server'
- 3-Sekunden-Gedrueckthalten-Button zur Bestaetigung (Fortschrittsbalken)
- Warnhinweis: Wird SOFORT geloescht, nicht rueckgaengig machbar
- Server: delete-music/delete-all Endpunkte mit Dedup-sicherem Registry-Cleanup
- Loeschen nur des eigenen Kontos (User aus Token, kein IDOR)
2026-07-31 15:39:19 +02:00

195 lines
6.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../services/cloud_service.dart';
import '../utils/farb_theme.dart';
import 'halte_zum_bestaetigen.dart';
/// Cloud Sync Einstellungen eigenständiger Dialog
class CloudEinstellungen extends StatefulWidget {
const CloudEinstellungen({super.key});
@override
State<CloudEinstellungen> createState() => _CloudEinstellungenState();
}
class _CloudEinstellungenState extends State<CloudEinstellungen> {
bool _autoSync = true;
int _syncIntervall = 6; // Stunden
Timer? _syncTimer;
@override
void initState() {
super.initState();
_ladeSettings();
}
@override
void dispose() {
_syncTimer?.cancel();
super.dispose();
}
Future<void> _ladeSettings() async {
final p = await SharedPreferences.getInstance();
if (mounted) {
setState(() {
_autoSync = p.getBool('cloud_auto') ?? true;
_syncIntervall = p.getInt('cloud_interval') ?? 6;
});
}
}
void _starteAutoSync() {
_syncTimer?.cancel();
if (!_autoSync || _syncIntervall == 0) return;
_syncTimer = Timer.periodic(
Duration(hours: _syncIntervall),
(_) => _triggerSync(),
);
}
void _triggerSync() {
// Wird vom CloudService in cloud_screen.dart erledigt
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: MeloTheme.dunkel1,
title: const Row(
children: [
Icon(Icons.cloud, color: MeloTheme.rot, size: 20),
SizedBox(width: 8),
Text('☁ Cloud Sync', style: TextStyle(color: Colors.white, fontSize: 16)),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
SwitchListTile(
title: const Text('Auto-Sync', style: TextStyle(color: Colors.white, fontSize: 14)),
subtitle: Text(_autoSync ? 'Aktiviert' : 'Aus',
style: const TextStyle(color: Colors.grey, fontSize: 12)),
value: _autoSync,
activeColor: MeloTheme.rot,
onChanged: (v) async {
setState(() => _autoSync = v);
final p = await SharedPreferences.getInstance();
p.setBool('cloud_auto', v);
_starteAutoSync();
},
),
const Divider(color: MeloTheme.dunkel2),
...['Manuell', 'Alle 3h', 'Alle 6h', 'Alle 12h'].asMap().entries.map((e) {
final vals = [0, 3, 6, 12];
final val = vals[e.key];
return RadioListTile<int>(
dense: true,
title: Text(e.value,
style: const TextStyle(color: Colors.white, fontSize: 13)),
value: val,
groupValue: _syncIntervall,
activeColor: MeloTheme.rot,
onChanged: (v) async {
if (v == null) return;
setState(() => _syncIntervall = v);
final p = await SharedPreferences.getInstance();
p.setInt('cloud_interval', v);
_starteAutoSync();
},
);
}),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
icon: const Icon(Icons.sync, size: 16, color: MeloTheme.rot),
label: const Text('Jetzt synchronisieren',
style: TextStyle(color: MeloTheme.rot, fontSize: 13)),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: MeloTheme.rot),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
onPressed: () {
Navigator.pop(context, true); // Signal zum Sync
},
),
),
if (CloudService().istAngemeldet) ...[
const Divider(color: MeloTheme.dunkel2),
Align(
alignment: Alignment.centerLeft,
child: Text('Cloud-Daten löschen',
style: TextStyle(color: Colors.red.shade300, fontSize: 12, fontWeight: FontWeight.w600)),
),
ListTile(
dense: true,
leading: const Icon(Icons.music_off, color: Colors.red, size: 18),
title: const Text('Nur Musik löschen',
style: TextStyle(color: Colors.white, fontSize: 13)),
subtitle: const Text('Alle Cloud-Songs entfernen',
style: TextStyle(color: Colors.grey, fontSize: 11)),
onTap: () => _zeigeLoeschDialog(komplett: false),
),
ListTile(
dense: true,
leading: const Icon(Icons.delete_sweep, color: Colors.red, size: 18),
title: const Text('Komplett vom Server löschen',
style: TextStyle(color: Colors.white, fontSize: 13)),
subtitle: const Text('Musik + Shares + Ordner (unwiderruflich)',
style: TextStyle(color: Colors.grey, fontSize: 11)),
onTap: () => _zeigeLoeschDialog(komplett: true),
),
],
],
),
);
}
/// Lösch-Dialog: Warnt vor sofortigem Löschen, Bestätigung per 3s-Halten.
void _zeigeLoeschDialog({required bool komplett}) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
backgroundColor: MeloTheme.dunkel1,
title: Text(komplett ? '☠️ Komplett löschen?' : '🎵 Musik löschen?',
style: const TextStyle(color: Colors.white, fontSize: 15)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'⚠️ Wird SOFORT gelöscht und kann nicht rückgängig gemacht werden!',
style: TextStyle(color: Colors.orangeAccent, fontSize: 13),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
HalteZumBestaetigen(
text: '3 Sekunden gedrückt halten…',
farbe: Colors.red,
onBestaetigt: () async {
Navigator.pop(ctx);
final ok = komplett
? await CloudService().loescheAlles()
: await CloudService().loescheMusik();
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(ok
? '✅ Cloud-Daten gelöscht'
: '❌ Löschen fehlgeschlagen bist du angemeldet?'),
));
},
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Abbrechen', style: TextStyle(color: Colors.grey)),
),
],
),
);
}
}