fix: code-review findings — error handling, null safety, persist settings, auth header

This commit is contained in:
Dustin
2026-08-01 15:09:48 +02:00
parent 669a02304e
commit 4a05c76199
4 changed files with 40 additions and 9 deletions
+16 -3
View File
@@ -43,7 +43,10 @@ class _CloudScreenState extends State<CloudScreen> {
Future<void> _verbindeCloud() async { Future<void> _verbindeCloud() async {
final user = AuthService().benutzer; final user = AuthService().benutzer;
if (user.isNotEmpty) { if (user.isNotEmpty) {
await widget.cloud.login(user); final ok = await widget.cloud.login(user);
if (!ok && mounted) {
setState(() => _setzeStatus('Cloud-Login fehlgeschlagen', ok: false));
}
} }
} }
@@ -91,6 +94,7 @@ class _CloudScreenState extends State<CloudScreen> {
Future<void> _upload() async { Future<void> _upload() async {
setState(() => _ladt = true); setState(() => _ladt = true);
_setzeStatus('Suche lokale Songs...'); _setzeStatus('Suche lokale Songs...');
try {
final dir = Directory('${(await getApplicationDocumentsDirectory()).path}/music'); final dir = Directory('${(await getApplicationDocumentsDirectory()).path}/music');
if (!await dir.exists()) { if (!await dir.exists()) {
setState(() { _ladt = false; _setzeStatus('Keine lokalen Songs', ok: false); }); setState(() { _ladt = false; _setzeStatus('Keine lokalen Songs', ok: false); });
@@ -110,11 +114,16 @@ class _CloudScreenState extends State<CloudScreen> {
_setzeStatus('$count Songs hochgeladen', ok: count > 0); _setzeStatus('$count Songs hochgeladen', ok: count > 0);
MeloLogger().aktion('cloud_upload', {'count': count}); MeloLogger().aktion('cloud_upload', {'count': count});
} }
} catch (e) {
MeloLogger().fehler('cloud_upload_path', e);
if (mounted) setState(() { _ladt = false; _setzeStatus('Fehler beim Upload', ok: false); });
}
} }
Future<void> _download() async { Future<void> _download() async {
setState(() => _ladt = true); setState(() { _ladt = true; });
_setzeStatus('Vergleiche mit Server...'); _setzeStatus('Vergleiche mit Server...');
try {
final dir = Directory('${(await getApplicationDocumentsDirectory()).path}/music'); final dir = Directory('${(await getApplicationDocumentsDirectory()).path}/music');
if (!await dir.exists()) await dir.create(recursive: true); if (!await dir.exists()) await dir.create(recursive: true);
final localFiles = dir.listSync().whereType<File>() final localFiles = dir.listSync().whereType<File>()
@@ -122,7 +131,7 @@ class _CloudScreenState extends State<CloudScreen> {
final serverSongs = await widget.cloud.listSongs(); final serverSongs = await widget.cloud.listSongs();
int downloaded = 0; int downloaded = 0;
for (final song in serverSongs) { for (final song in serverSongs) {
final title = song['title'].toString(); final title = (song['title'] ?? 'unknown').toString();
if (localFiles.contains(title)) continue; if (localFiles.contains(title)) continue;
_setzeStatus('Download: $title...'); _setzeStatus('Download: $title...');
final sid = song['id'].toString(); final sid = song['id'].toString();
@@ -135,6 +144,10 @@ class _CloudScreenState extends State<CloudScreen> {
_setzeStatus('$downloaded Songs heruntergeladen', ok: true); _setzeStatus('$downloaded Songs heruntergeladen', ok: true);
MeloLogger().aktion('cloud_download', {'count': downloaded}); MeloLogger().aktion('cloud_download', {'count': downloaded});
} }
} catch (e) {
MeloLogger().fehler('cloud_download_path', e);
if (mounted) setState(() { _ladt = false; _setzeStatus('Fehler beim Download', ok: false); });
}
} }
void _setzeStatus(String msg, {bool ok = false}) { void _setzeStatus(String msg, {bool ok = false}) {
-1
View File
@@ -9,7 +9,6 @@ import '../widgets/melo_header.dart';
import '../widgets/statistik_card.dart'; import '../widgets/statistik_card.dart';
import '../widgets/tag_leiste.dart'; import '../widgets/tag_leiste.dart';
import '../widgets/song_tile.dart'; import '../widgets/song_tile.dart';
import '../widgets/navidrome_browser.dart';
import '../widgets/playlist_sheet.dart'; import '../widgets/playlist_sheet.dart';
import 'download_screen.dart'; import 'download_screen.dart';
import 'cloud_screen.dart'; import 'cloud_screen.dart';
+15 -1
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../config/app_config.dart'; import '../config/app_config.dart';
import '../utils/farb_theme.dart'; import '../utils/farb_theme.dart';
import 'cloud_screen.dart'; import 'cloud_screen.dart';
@@ -15,6 +16,17 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
bool _diagnose = AppConfig.sendeDiagnosedaten; bool _diagnose = AppConfig.sendeDiagnosedaten;
@override
void initState() {
super.initState();
_ladeDiagnose();
}
Future<void> _ladeDiagnose() async {
final p = await SharedPreferences.getInstance();
if (mounted) setState(() => _diagnose = p.getBool('diagnose_daten') ?? AppConfig.sendeDiagnosedaten);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -84,9 +96,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
value: _diagnose, value: _diagnose,
activeThumbColor: MeloTheme.rot, activeThumbColor: MeloTheme.rot,
onChanged: (v) { onChanged: (v) async {
setState(() => _diagnose = v); setState(() => _diagnose = v);
AppConfig.sendeDiagnosedaten = v; AppConfig.sendeDiagnosedaten = v;
final p = await SharedPreferences.getInstance();
p.setBool('diagnose_daten', v);
}, },
secondary: const Icon(Icons.bug_report_outlined, color: MeloTheme.rot, size: 22), secondary: const Icon(Icons.bug_report_outlined, color: MeloTheme.rot, size: 22),
), ),
+9 -4
View File
@@ -21,10 +21,15 @@ class AuthService {
String? get token => _token; String? get token => _token;
/// Auth-Header für API-Requests /// Auth-Header für API-Requests
Map<String, String> get authHeader => { Map<String, String> get authHeader {
'Authorization': 'Bearer ${_token ?? ''}', final headers = <String, String>{
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
if (_token != null && _token!.isNotEmpty) {
headers['Authorization'] = 'Bearer $_token';
}
return headers;
}
/// Lädt gespeicherten Token beim App-Start /// Lädt gespeicherten Token beim App-Start
Future<void> initialisieren() async { Future<void> initialisieren() async {