Tinker-Feedback: 5 Bugfixes (Shuffle/Repeat, Full-Cover, Auto-Play, Scan-Button, Back-Geste) #6
@@ -5,6 +5,38 @@ Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### 🔑 Manuelle Baka-Anmeldung übernahm das neue Passwort nie dauerhaft; Auto-Login-Fehler wurde verworfen (2026-08-29)
|
||||||
|
|
||||||
|
- 🐛 **Wer sich nach einer fehlgeschlagenen Auto-Anmeldung manuell im
|
||||||
|
"Baka-Konto"-Dialog mit dem korrekten (neuen) Passwort anmeldete, bekam
|
||||||
|
denselben Fehler beim nächsten App-Start (nach Ablauf des 7-Tage-Tokens)
|
||||||
|
garantiert wieder** — unabhängig vom Server-Zustand. Grund: Navidrome-
|
||||||
|
und Baka-Konto teilen sich laut Design dieselben Zugangsdaten
|
||||||
|
(`navidrome_service.dart`), aber der manuelle Anmelde-Dialog
|
||||||
|
(`_AnmeldeDialogState._anmelden()` in `downloads_screen.dart`) schrieb
|
||||||
|
das eingegebene Passwort nur ins `BakaAuth`-Token — nie zurück in die
|
||||||
|
lokale Navidrome-Zugangsdaten-Ablage (`NavidromeService`), aus der
|
||||||
|
`BakaAuth.autoAnmelden()` sich beim nächsten Start bedient. Der manuelle
|
||||||
|
Login "half" also nur bis zum nächsten Token-Ablauf.
|
||||||
|
- 🔧 **Fix:** Nach erfolgreichem manuellem Login persistiert der Dialog die
|
||||||
|
neuen Zugangsdaten jetzt zusätzlich über
|
||||||
|
`NavidromeService.speichereZugangsdaten(...)`. Dafür bekam
|
||||||
|
`NavidromeService` einen fehlenden öffentlichen Getter `serverUrl`.
|
||||||
|
- 🐛 **`BakaAuth.autoAnmelden()` verwarf den Fehlertext von `anmelden()`
|
||||||
|
komplett** — aus der App war nicht diagnostizierbar, ob ein Auto-Login
|
||||||
|
an falschem Passwort, Rate-Limit oder Verbindungsfehler scheiterte.
|
||||||
|
- 🔧 **Fix:** neues Feld `BakaAuth.letzterAutoLoginFehler` hält den
|
||||||
|
Fehlertext des letzten Auto-Login-Versuchs fest (zusätzlich per
|
||||||
|
`debugPrint` geloggt) — die bestehende Race-Condition-Absicherung über
|
||||||
|
`_laufenderAutoLogin` bleibt unverändert erhalten.
|
||||||
|
- ✅ **Getestet (RED→GREEN):** neuer Widget-Test in `online_screen_test.dart`
|
||||||
|
bestätigt, dass `nav.passwort`/`nav.benutzer` nach erfolgreichem
|
||||||
|
manuellem Login den neu eingegebenen Wert zeigen; neuer Unit-Test in
|
||||||
|
`baka_auth_test.dart` bestätigt, dass `letzterAutoLoginFehler` nach
|
||||||
|
einem fehlgeschlagenen `autoAnmelden()` den Fehlertext trägt statt
|
||||||
|
`null` zu bleiben. Volle Suite (601 Tests) grün, `flutter analyze` ohne
|
||||||
|
Befund.
|
||||||
|
|
||||||
### 🎧 "Zuletzt" zeigte neu Hinzugefügtes statt zuletzt Gehörtem; Herz saß fern vom Songnamen (2026-08-29)
|
### 🎧 "Zuletzt" zeigte neu Hinzugefügtes statt zuletzt Gehörtem; Herz saß fern vom Songnamen (2026-08-29)
|
||||||
|
|
||||||
- 🐛 **Der Schnellzugriff "Zuletzt" auf "Meine Musik" zeigte "zuletzt hinzugefügte" Songs,
|
- 🐛 **Der Schnellzugriff "Zuletzt" auf "Meine Musik" zeigte "zuletzt hinzugefügte" Songs,
|
||||||
|
|||||||
@@ -903,6 +903,14 @@ class _AnmeldeDialogState extends State<_AnmeldeDialog> {
|
|||||||
final fehler = await auth.anmelden(_benutzer.text.trim(), _passwort.text);
|
final fehler = await auth.anmelden(_benutzer.text.trim(), _passwort.text);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (fehler == null) {
|
if (fehler == null) {
|
||||||
|
// Navidrome- und Baka-Konto teilen sich die Zugangsdaten (siehe
|
||||||
|
// navidrome_service.dart) — ohne diese Übernahme würde der nächste
|
||||||
|
// Auto-Login nach Ablauf des Baka-Tokens wieder mit dem alten
|
||||||
|
// Passwort scheitern.
|
||||||
|
final nav = context.read<DownloadService>().navidrome;
|
||||||
|
await nav.speichereZugangsdaten(
|
||||||
|
nav.serverUrl, _benutzer.text.trim(), _passwort.text);
|
||||||
|
if (!mounted) return;
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ class BakaAuth extends ChangeNotifier {
|
|||||||
/// lösen beim App-Start gleichzeitig autoAnmelden aus).
|
/// lösen beim App-Start gleichzeitig autoAnmelden aus).
|
||||||
Future<void>? _laufenderAutoLogin;
|
Future<void>? _laufenderAutoLogin;
|
||||||
|
|
||||||
|
/// Fehlertext des letzten automatischen Anmeldeversuchs — für Diagnose,
|
||||||
|
/// da autoAnmelden() (anders als anmelden()) keinen Rückgabewert hat.
|
||||||
|
String? letzterAutoLoginFehler;
|
||||||
|
|
||||||
bool get istAngemeldet => _token != null && _token!.isNotEmpty;
|
bool get istAngemeldet => _token != null && _token!.isNotEmpty;
|
||||||
String get benutzer => _benutzer;
|
String get benutzer => _benutzer;
|
||||||
|
|
||||||
@@ -114,11 +118,19 @@ class BakaAuth extends ChangeNotifier {
|
|||||||
/// authentifizierten Aufruf ohnehin automatisch.
|
/// authentifizierten Aufruf ohnehin automatisch.
|
||||||
Future<void> autoAnmelden(String benutzer, String passwort) async {
|
Future<void> autoAnmelden(String benutzer, String passwort) async {
|
||||||
if (istAngemeldet || benutzer.isEmpty || passwort.isEmpty) return;
|
if (istAngemeldet || benutzer.isEmpty || passwort.isEmpty) return;
|
||||||
_laufenderAutoLogin ??= anmelden(benutzer, passwort)
|
_laufenderAutoLogin ??= _autoAnmeldenIntern(benutzer, passwort)
|
||||||
.whenComplete(() => _laufenderAutoLogin = null);
|
.whenComplete(() => _laufenderAutoLogin = null);
|
||||||
await _laufenderAutoLogin;
|
await _laufenderAutoLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _autoAnmeldenIntern(String benutzer, String passwort) async {
|
||||||
|
final fehler = await anmelden(benutzer, passwort);
|
||||||
|
letzterAutoLoginFehler = fehler;
|
||||||
|
if (fehler != null) {
|
||||||
|
debugPrint('Auto-Anmeldung fehlgeschlagen: $fehler');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> abmelden() async {
|
Future<void> abmelden() async {
|
||||||
_token = null;
|
_token = null;
|
||||||
_benutzer = '';
|
_benutzer = '';
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ class NavidromeService {
|
|||||||
/// Zugangsdaten.
|
/// Zugangsdaten.
|
||||||
String get benutzer => _user;
|
String get benutzer => _user;
|
||||||
String get passwort => _password;
|
String get passwort => _password;
|
||||||
|
String get serverUrl => _serverUrl;
|
||||||
|
|
||||||
/// Dekodiert eine Subsonic-JSON-Antwort und wirft [NavidromeException],
|
/// Dekodiert eine Subsonic-JSON-Antwort und wirft [NavidromeException],
|
||||||
/// wenn der Server `status:"failed"` meldet (z. B. falsches Passwort) —
|
/// wenn der Server `status:"failed"` meldet (z. B. falsches Passwort) —
|
||||||
|
|||||||
@@ -268,6 +268,39 @@ void main() {
|
|||||||
expect(feld.controller!.text, 'Tinker');
|
expect(feld.controller!.text, 'Tinker');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'Erfolgreiches manuelles Anmelden übernimmt das neue Passwort auch '
|
||||||
|
'für Navidrome — sonst kommt der Auto-Login-Fehler nach Ablauf des '
|
||||||
|
'Baka-Tokens garantiert wieder', (tester) async {
|
||||||
|
final auth = BakaAuth(
|
||||||
|
client: MockClient((request) async {
|
||||||
|
final body = jsonDecode(request.body) as Map<String, dynamic>;
|
||||||
|
if (body['password'] == 'neuesPasswort') {
|
||||||
|
return http.Response(jsonEncode({'token': 'jwt-neu'}), 200);
|
||||||
|
}
|
||||||
|
return http.Response('', 500);
|
||||||
|
}),
|
||||||
|
speicher: _MemorySpeicher(),
|
||||||
|
);
|
||||||
|
final nav = NavidromeService()
|
||||||
|
..setCredentials('https://musik.baka-net.de', 'Tinker', 'altesPasswort');
|
||||||
|
|
||||||
|
await tester.pumpWidget(_wrap(auth,
|
||||||
|
db: db, categories: categories, navidrome: nav));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Beim Baka-Konto anmelden'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.enterText(find.byType(TextField).last, 'neuesPasswort');
|
||||||
|
await tester.tap(find.text('Anmelden'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(nav.passwort, 'neuesPasswort');
|
||||||
|
expect(nav.benutzer, 'Tinker');
|
||||||
|
expect(auth.istAngemeldet, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'Server-User meldet sich im Hintergrund mit denselben Zugangsdaten '
|
'Server-User meldet sich im Hintergrund mit denselben Zugangsdaten '
|
||||||
'beim Baka-Konto an', (tester) async {
|
'beim Baka-Konto an', (tester) async {
|
||||||
|
|||||||
@@ -139,6 +139,20 @@ void main() {
|
|||||||
expect(auth.benutzer, 'Tinker');
|
expect(auth.benutzer, 'Tinker');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'autoAnmelden merkt sich den Fehlergrund für die Diagnose, da es '
|
||||||
|
'selbst keinen Rückgabewert liefert', () async {
|
||||||
|
final client = MockClient((_) async => http.Response(
|
||||||
|
jsonEncode({'status': 'error', 'message': 'Falsches Passwort'}),
|
||||||
|
200));
|
||||||
|
final auth = BakaAuth(client: client, speicher: _MemorySpeicher());
|
||||||
|
|
||||||
|
await auth.autoAnmelden('Tinker', 'falsch');
|
||||||
|
|
||||||
|
expect(auth.letzterAutoLoginFehler, 'Falsches Passwort');
|
||||||
|
expect(auth.istAngemeldet, isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
test('autoAnmelden tut nichts ohne Zugangsdaten', () async {
|
test('autoAnmelden tut nichts ohne Zugangsdaten', () async {
|
||||||
var aufrufe = 0;
|
var aufrufe = 0;
|
||||||
final client = MockClient((_) async {
|
final client = MockClient((_) async {
|
||||||
|
|||||||
Reference in New Issue
Block a user