CRIT: - Tag-Leiste: Toggle-Kopf 'Tags & Filter' eingebaut (war unerreichbar) + TagStats - Profil: Cloud-Count vor Dialog aufloesen (kein 'Instance of Future' mehr) HIGH: - CloudEinstellungen: toten Sync-Timer entfernt (echter Timer im ViewModel) - StatistikCard + RecentWidget jetzt eingebaut (gesamtMB/gesamtMin endlich genutzt) - CloudService als Singleton (konsistenter Token-Zustand in allen Services) MED/LOW: - Playlist-Erkennung nur noch via list= Parameter - Player: fehlende Quelle wird geloggt statt still - song_tile: null-ID-Guard vor Tag-Dialog - Scanner-Log mit Exception-Objekt - FavoritenService: anzahlFavoriten() fuer StatistikCard
175 lines
6.5 KiB
Dart
175 lines
6.5 KiB
Dart
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
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_ladeSettings();
|
||
}
|
||
|
||
Future<void> _ladeSettings() async {
|
||
final p = await SharedPreferences.getInstance();
|
||
if (mounted) {
|
||
setState(() {
|
||
_autoSync = p.getBool('cloud_auto') ?? true;
|
||
_syncIntervall = p.getInt('cloud_interval') ?? 6;
|
||
});
|
||
}
|
||
}
|
||
// Hinweis: Der echte Auto-Sync-Timer läuft im MeloHomeViewModel
|
||
// (_starteCloudSyncScheduler). Dieser Dialog speichert nur die Einstellungen;
|
||
// die Änderungen greifen beim nächsten App-Start bzw. über "Jetzt synchronisieren".
|
||
|
||
@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();
|
||
await p.setBool('cloud_auto', v);
|
||
},
|
||
),
|
||
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();
|
||
await p.setInt('cloud_interval', v);
|
||
},
|
||
);
|
||
}),
|
||
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)),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|