From 2948285d82316034895f7f0cd91ba65527853e09 Mon Sep 17 00:00:00 2001 From: "Hermes (Server)" Date: Wed, 19 Aug 2026 21:12:38 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Feature:=20Login-Verbesserungen=20f?= =?UTF-8?q?=C3=BCr=20Dustin!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 🔗 AUTO-URL: Server-URL \"https://musik.baka-net.de\" vorausgefüllt (editierbar) 2. 👤 USER-BUTTONS: Baka/Tinker/Dustin als Button-Auswahl (statt Text-Input) 3. 🔐 PASSWORT-ONLY: Dialog zeigt nur Passwort-Feld + User-Auswahl (wie beim Register!) 4. 💡 GAST-MODUS: Info-Text erklärt lokale Nutzung ohne Server-Konto → App funktioniert auch komplett offline mit lokaler Musikbibliothek! UX-Verbesserungen: - Weniger zu tippen (URL + User vorgegeben) - Schneller Login für Baka/Tinker/Dustin - Klare Fallback-Option für lokale Nutzung Co-Authored-By: Claude Haiku 4.5 Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru --- lib/settings/settings_screen.dart | 153 +++++++++++++++++++----------- 1 file changed, 96 insertions(+), 57 deletions(-) diff --git a/lib/settings/settings_screen.dart b/lib/settings/settings_screen.dart index c9f95dd..912fc1f 100644 --- a/lib/settings/settings_screen.dart +++ b/lib/settings/settings_screen.dart @@ -79,6 +79,14 @@ class _SettingsScreenState extends State { onPressed: () => _zeigeNavidromeDialog(), ), ), + if (!_navidrome.istVerbunden) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Text( + '💡 Tipp: Oder nutze Melo lokal ohne Server-Konto (nur deine Musik auf dem Gerät)', + style: TextStyle(fontSize: 12, color: Colors.white54), + ), + ), ListTile( leading: const Icon(Icons.wifi_off), title: const Text('📴 Offline-Modus'), @@ -147,9 +155,9 @@ class _SettingsScreenState extends State { } void _zeigeNavidromeDialog() { - final urlCtrl = TextEditingController(); - final userCtrl = TextEditingController(); + final urlCtrl = TextEditingController(text: 'https://musik.baka-net.de'); final passCtrl = TextEditingController(); + String selectedUser = 'Dustin'; bool verbindet = false; String? fehler; @@ -158,59 +166,91 @@ class _SettingsScreenState extends State { builder: (ctx) => StatefulBuilder( builder: (ctx, setDialogState) => AlertDialog( backgroundColor: Colors.grey.shade900, - title: const Text('🌐 Navidrome Verbinden', + title: const Text('🌐 Navidrome Login', style: TextStyle(color: Colors.white, fontSize: 16)), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: urlCtrl, - style: const TextStyle(color: Colors.white), - decoration: const InputDecoration( - labelText: 'Server-URL', - hintText: 'https://musik.example.com', - labelStyle: TextStyle(color: Colors.grey), - hintStyle: TextStyle(color: Colors.grey), - border: OutlineInputBorder(), - ), - ), - const SizedBox(height: 8), - TextField( - controller: userCtrl, - style: const TextStyle(color: Colors.white), - decoration: const InputDecoration( - labelText: 'Benutzer', - labelStyle: TextStyle(color: Colors.grey), - border: OutlineInputBorder(), - ), - ), - const SizedBox(height: 8), - TextField( - controller: passCtrl, - style: const TextStyle(color: Colors.white), - obscureText: true, - decoration: const InputDecoration( - labelText: 'Passwort', - labelStyle: TextStyle(color: Colors.grey), - border: OutlineInputBorder(), - ), - ), - if (fehler != null) ...[ - const SizedBox(height: 8), - Text(fehler!, - style: const TextStyle(color: Colors.red, fontSize: 12)), - ], - if (verbindet) - const Padding( - padding: EdgeInsets.only(top: 12), - child: SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, color: Colors.redAccent), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: urlCtrl, + style: const TextStyle(color: Colors.white), + decoration: const InputDecoration( + labelText: 'Server-URL', + labelStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder(), ), ), - ], + const SizedBox(height: 12), + const Text('Benutzer:', style: TextStyle(fontSize: 12, color: Colors.white70)), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: FilledButton( + onPressed: () => setDialogState(() => selectedUser = 'Baka'), + style: FilledButton.styleFrom( + backgroundColor: selectedUser == 'Baka' + ? Colors.redAccent + : Colors.grey.shade700, + ), + child: const Text('Baka'), + ), + ), + const SizedBox(width: 8), + Expanded( + child: FilledButton( + onPressed: () => setDialogState(() => selectedUser = 'Tinker'), + style: FilledButton.styleFrom( + backgroundColor: selectedUser == 'Tinker' + ? Colors.redAccent + : Colors.grey.shade700, + ), + child: const Text('Tinker'), + ), + ), + const SizedBox(width: 8), + Expanded( + child: FilledButton( + onPressed: () => setDialogState(() => selectedUser = 'Dustin'), + style: FilledButton.styleFrom( + backgroundColor: selectedUser == 'Dustin' + ? Colors.redAccent + : Colors.grey.shade700, + ), + child: const Text('Dustin'), + ), + ), + ], + ), + const SizedBox(height: 12), + TextField( + controller: passCtrl, + style: const TextStyle(color: Colors.white), + obscureText: true, + decoration: const InputDecoration( + labelText: 'Passwort', + labelStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder(), + ), + ), + if (fehler != null) ...[ + const SizedBox(height: 8), + Text(fehler!, + style: const TextStyle(color: Colors.red, fontSize: 12)), + ], + if (verbindet) + const Padding( + padding: EdgeInsets.only(top: 12), + child: SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.redAccent), + ), + ), + ], + ), ), actions: [ TextButton( @@ -222,20 +262,20 @@ class _SettingsScreenState extends State { : () async { setDialogState(() => verbindet = true); _navidrome.setCredentials( - urlCtrl.text, userCtrl.text, passCtrl.text); + urlCtrl.text, selectedUser, passCtrl.text); final ok = await _navidrome.ping(); setDialogState(() => verbindet = false); if (ok && ctx.mounted) { await _navidrome.speichereZugangsdaten( - urlCtrl.text, userCtrl.text, passCtrl.text); + urlCtrl.text, selectedUser, passCtrl.text); if (ctx.mounted) Navigator.pop(ctx); if (mounted) setState(() {}); } else if (ctx.mounted) { setDialogState(() => - fehler = '❌ Verbindung fehlgeschlagen\nPrüfe URL + Zugangsdaten'); + fehler = '❌ Login fehlgeschlagen'); } }, - child: const Text('Verbinden', + child: const Text('Login', style: TextStyle(color: Colors.redAccent)), ), ], @@ -243,7 +283,6 @@ class _SettingsScreenState extends State { ), ).then((_) { urlCtrl.dispose(); - userCtrl.dispose(); passCtrl.dispose(); }); }