fix(dictionary): words held in topics, a chosen destination, faster adds
A word counted as held only in the studyset itself, so adding from a
studyset whose topics held it (Vocabulary, its words in N3) made a
second card, and watching from a top-level studyset put every new word
into it. Now a word is held anywhere in the studyset and its topics,
archived ones too, and its progress shows from that card; new words go
where the person chose for the studyset ("Adds to N3" in the dictionary
and the Watch screen, the studyset and its topics but archived ones,
kept in their settings, schema 10), the studyset itself until then.
Adding a word took about two seconds on 17,000 card tags: each of its
four tags read every card's tags twice. They are now written with one
read, the Watch screen shows the word added at once and takes it back
should writing fail, and it no longer reads the studyset's progress
again after each word. On a copy of a real database an Add takes 0.3 to
0.4 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
5efa552489
commit
6737a42ff7
1 changed files with 954 additions and 77 deletions
|
|
@ -15,8 +15,10 @@ import 'package:halkyon_learn_flutter/src/feature/dictionary/model/dictionary.da
|
|||
import 'package:halkyon_learn_flutter/src/feature/dictionary/model/sentence_words.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/jellyfin/model/jellyfin.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/jellyfin/widget/watch_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/widget/study_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/tag/data/tag_repository.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
|
@ -26,6 +28,7 @@ import '../../support/fake_device_dictionaries.dart';
|
|||
import '../../support/fake_dictionary_gateway.dart';
|
||||
import '../../support/fake_jellyfin.dart';
|
||||
import '../../support/fake_review_gateway.dart';
|
||||
import '../../support/fake_settings_gateway.dart';
|
||||
import '../../support/fake_studyset_gateway.dart';
|
||||
import '../../support/fake_tag_gateway.dart';
|
||||
import '../../support/pump_app.dart';
|
||||
|
|
@ -39,8 +42,23 @@ void main() {
|
|||
late TagRepository tags;
|
||||
late FakeJellyfinGateway jellyfin;
|
||||
late FakeMediaPlayers players;
|
||||
late FakeStudysetGateway studysets;
|
||||
late FakeSettingsGateway settingsGateway;
|
||||
|
||||
setUp(() {
|
||||
studysets = FakeStudysetGateway()
|
||||
..receiveRemoteChange(
|
||||
'japanese',
|
||||
encodeStudysetPayload(
|
||||
StudysetContent(
|
||||
title: 'Japanese',
|
||||
description: RichContent.empty(),
|
||||
frontLanguage: 'ja',
|
||||
backLanguage: 'en',
|
||||
),
|
||||
),
|
||||
);
|
||||
settingsGateway = FakeSettingsGateway();
|
||||
cardGateway = FakeCardGateway();
|
||||
tagGateway = FakeTagGateway();
|
||||
cards = CardRepository(cardGateway);
|
||||
|
|
@ -86,18 +104,6 @@ void main() {
|
|||
tester.view.physicalSize = size;
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
final studysets = FakeStudysetGateway()
|
||||
..receiveRemoteChange(
|
||||
'japanese',
|
||||
encodeStudysetPayload(
|
||||
StudysetContent(
|
||||
title: 'Japanese',
|
||||
description: RichContent.empty(),
|
||||
frontLanguage: 'ja',
|
||||
backLanguage: 'en',
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!playerAvailable) {
|
||||
players = FakeMediaPlayers(available: false);
|
||||
}
|
||||
|
|
@ -105,6 +111,7 @@ void main() {
|
|||
tester,
|
||||
testDependencies(
|
||||
studysetGateway: studysets,
|
||||
settingsGateway: settingsGateway,
|
||||
cardGateway: cardGateway,
|
||||
tagGateway: tagGateway,
|
||||
dictionaryGateway:
|
||||
|
|
@ -123,6 +130,14 @@ void main() {
|
|||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Adds words as the screen does, into [studysets].
|
||||
DictionaryAdder adder() => DictionaryAdder(
|
||||
cards,
|
||||
tags,
|
||||
StudysetRepository(studysets),
|
||||
SettingsRepository(settingsGateway),
|
||||
);
|
||||
|
||||
/// Plays on to [position].
|
||||
Future<void> playTo(WidgetTester tester, Duration position) async {
|
||||
players.last!.moveTo(position);
|
||||
|
|
@ -537,7 +552,7 @@ void main() {
|
|||
testWidgets('a word the studyset holds says how far it is learnt, in the '
|
||||
'list of the words it holds', (tester) async {
|
||||
final station = sampleWords.firstWhere((word) => word.headword == '駅');
|
||||
await DictionaryAdder(cards, tags).add('japanese', [station]);
|
||||
await adder().add('japanese', [station]);
|
||||
final card = (await cards.cardsOf('japanese')).single;
|
||||
final reviews = FakeReviewGateway();
|
||||
await reviews.insert([
|
||||
|
|
@ -561,6 +576,55 @@ void main() {
|
|||
expect(find.textContaining('Learned · again'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets("watching from a studyset with topics, a topic's word shows "
|
||||
'as held, and a new one goes where Adds to says, shown as added at '
|
||||
'once', (tester) async {
|
||||
studysets.receiveRemoteChange(
|
||||
'n3',
|
||||
encodeStudysetPayload(StudysetContent.titled('N3')),
|
||||
parentId: 'japanese',
|
||||
);
|
||||
final station = sampleWords.firstWhere((word) => word.headword == '駅');
|
||||
await adder().add('n3', [station]);
|
||||
await watch(tester);
|
||||
|
||||
await tester.tap(find.byTooltip('Filter the words'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Words in the studyset'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('駅', findRichText: true), findsOneWidget);
|
||||
// The filter's menu stays open for more choices.
|
||||
await tester.tap(find.byTooltip('Filter the words'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Adds to Japanese'));
|
||||
await tester.pumpAndSettle();
|
||||
// The words' levels say N3 too.
|
||||
await tester.tap(find.widgetWithText(MenuItemButton, 'N3'));
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Adds to N3'), findsOneWidget);
|
||||
|
||||
await players.last!.play();
|
||||
await playTo(tester, const Duration(seconds: 2));
|
||||
await tester.tap(find.text('いました'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Add'));
|
||||
await tester.pump();
|
||||
// Held and new at once, before the card is written.
|
||||
expect(find.text('Add'), findsNothing);
|
||||
expect(find.text('New'), findsOneWidget);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('会う added'), findsOneWidget);
|
||||
expect(
|
||||
[for (final card in await cards.cardsOf('n3')) card.content.entry?.id],
|
||||
containsAll(['6']),
|
||||
);
|
||||
expect(await cards.cardsOf('japanese'), isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('the filter narrows the words by their class', (tester) async {
|
||||
await watch(tester);
|
||||
expect(find.textContaining('駅', findRichText: true), findsOneWidget);
|
||||
|
|
@ -752,7 +816,7 @@ void main() {
|
|||
testWidgets("the episode's words can be studied or crammed: the cards of "
|
||||
'its words, held before it was watched too, get its tag', (tester) async {
|
||||
final station = sampleWords.firstWhere((word) => word.headword == '駅');
|
||||
await DictionaryAdder(cards, tags).add('japanese', [station]);
|
||||
await adder().add('japanese', [station]);
|
||||
final card = (await cards.cardsOf('japanese')).single;
|
||||
await watch(tester);
|
||||
await tester.tap(find.byTooltip('Play'));
|
||||
|
|
|
|||
Loading…
Reference in a new issue