feat(learn): search ranks titles first; privacy changes show progress
- The Library's search lists titles starting with what is typed first, then titles holding it as a word, then secondary titles, rather than by the shelves' recency. - The offer to make every studyset private is gone: each studyset's lock already says and changes it, and new ones are private. - Making a studyset private or not from its sheet flips the switch as soon as the person agrees and says that everything in it is being written anew, rather than standing still for a minute. - Jellyfin's password can be shown while typed. Whats-New: Searching the library lists title matches first; changing a studyset's privacy shows its progress; Jellyfin's password can be shown. Whats-New-De: Die Suche in der Bibliothek zeigt Treffer im Titel zuerst; das Ändern der Privatsphäre eines Lernsets zeigt den Fortschritt; das Jellyfin-Passwort lässt sich anzeigen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
29f900992b
commit
1cc27ef0d6
2 changed files with 276 additions and 235 deletions
|
|
@ -104,6 +104,16 @@ void main() {
|
|||
expect(find.text('Living room'), findsOneWidget);
|
||||
await tester.enterText(find.widgetWithText(TextField, 'User name'), 'mika');
|
||||
await tester.enterText(find.widgetWithText(TextField, 'Password'), 'wrong');
|
||||
bool hidden() => tester
|
||||
.widget<TextField>(find.widgetWithText(TextField, 'Password'))
|
||||
.obscureText;
|
||||
expect(hidden(), isTrue);
|
||||
await tester.tap(find.byTooltip('Show password'));
|
||||
await tester.pump();
|
||||
expect(hidden(), isFalse);
|
||||
await tester.tap(find.byTooltip('Hide password'));
|
||||
await tester.pump();
|
||||
expect(hidden(), isTrue);
|
||||
await tester.tap(find.text('Sign in'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('The user name or password is wrong.'), findsOneWidget);
|
||||
|
|
|
|||
|
|
@ -683,6 +683,44 @@ void main() {
|
|||
expect(find.text('Kanji'), findsNothing, reason: 'the archive is closed');
|
||||
});
|
||||
|
||||
testWidgets('a search lists titles starting with what is typed first, then '
|
||||
'those holding it as a word, then secondary titles, whatever was changed '
|
||||
'last', (tester) async {
|
||||
stored(
|
||||
'tooling',
|
||||
'Rust',
|
||||
secondaryTitle: 'Python tooling, from the docs',
|
||||
updatedAt: DateTime.utc(2026, 11, 9),
|
||||
);
|
||||
stored(
|
||||
'learning',
|
||||
'Learning Python',
|
||||
updatedAt: DateTime.utc(2026, 11, 8),
|
||||
);
|
||||
stored('python', 'Python', updatedAt: DateTime.utc(2026, 1, 2));
|
||||
await showLibrary(tester);
|
||||
|
||||
await tester.tap(find.byTooltip('Search'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byType(TextField), 'python');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// In reading order: row by row, each from the start.
|
||||
final places = [
|
||||
for (final title in ['Python', 'Learning Python', 'Rust'])
|
||||
tester.getTopLeft(find.text(title)),
|
||||
];
|
||||
for (final (before, after) in [
|
||||
(places[0], places[1]),
|
||||
(places[1], places[2]),
|
||||
]) {
|
||||
expect(
|
||||
before.dy < after.dy || (before.dy == after.dy && before.dx < after.dx),
|
||||
isTrue,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('says when no studyset matches the search', (tester) async {
|
||||
stored('japanese', 'Japanese');
|
||||
await showLibrary(tester);
|
||||
|
|
@ -748,11 +786,45 @@ void main() {
|
|||
expect(gateway.privacyChanges, [('japanese', true)]);
|
||||
expect(tester.widget<SwitchListTile>(private).value, isTrue);
|
||||
|
||||
// Made readable again while that takes a while: the switch follows at
|
||||
// once, says what goes on, and takes no other change meanwhile.
|
||||
final hold = gateway.holdPrivacy = Completer<void>();
|
||||
await tester.tap(private);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Make non-private'));
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
expect(tester.widget<SwitchListTile>(private).value, isFalse);
|
||||
expect(tester.widget<SwitchListTile>(private).onChanged, isNull);
|
||||
expect(
|
||||
find.textContaining('Making everything in it readable'),
|
||||
findsOneWidget,
|
||||
);
|
||||
hold.complete();
|
||||
gateway.holdPrivacy = null;
|
||||
await tester.pumpAndSettle();
|
||||
expect(gateway.privacyChanges, [('japanese', true), ('japanese', false)]);
|
||||
expect(
|
||||
find.textContaining('Making everything in it readable'),
|
||||
findsNothing,
|
||||
);
|
||||
expect(tester.widget<SwitchListTile>(private).value, isFalse);
|
||||
|
||||
await tester.tap(private);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Make private'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.widget<SwitchListTile>(private).value, isTrue);
|
||||
|
||||
await tester.tap(private);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Cancel'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(gateway.privacyChanges, [('japanese', true)], reason: 'cancelled');
|
||||
expect(gateway.privacyChanges, [
|
||||
('japanese', true),
|
||||
('japanese', false),
|
||||
('japanese', true),
|
||||
], reason: 'cancelled');
|
||||
expect(tester.widget<SwitchListTile>(private).value, isTrue);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue