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) 8b6a04ead8 Fix: Review-Findings korrigiert (Tag-Leiste, Profil, Singleton u.a.)
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
2026-07-31 15:42:54 +02:00

175 lines
6.5 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 '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)),
),
],
),
);
}
}