feat(learn): spelling and grammar checked as people write (ADR 0024)
Where the instance's LanguageTool checks the language, the editors underline what it finds after a pause, a paragraph at a time, a tap offering its suggestions (GrammarHost, GrammarCheckController, GrammarOverlay; nothing written into the document). A card's front is checked in the studyset's front language, its back and answers in the back's, everything else in the language recognised. In a private studyset nothing is sent until the person agrees for it, asked once in a banner above the editor and kept in their settings (an agreement under privateAgreements, a refusal in grammarDeclined, settings schema 14), taken back in the studyset's privacy dialog. Settings > Writing turns it off on the device. The field's Check asks LanguageTool first where it checks the studyset's language, in a sheet offering to ask the assistant why; elsewhere it asks the assistant as before (amends ADR 0021, D54). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
f766a880ac
commit
d2eaef255e
2 changed files with 2986 additions and 32 deletions
|
|
@ -692,4 +692,82 @@ void main() {
|
|||
expect(both.foldedWith(both), both);
|
||||
});
|
||||
});
|
||||
|
||||
group('checking what is written in a private studyset (ADR 0024)', () {
|
||||
test('is asked until answered; agreed, it is an agreement as an '
|
||||
"assistant's, declined it is declined, and taken back it is asked "
|
||||
'again', () {
|
||||
const settings = PersonSettings();
|
||||
expect(settings.grammarAnswerOf('ja'), isNull);
|
||||
|
||||
final agreed = settings.withGrammarAnswer('ja', check: true);
|
||||
expect(agreed.grammarAnswerOf('ja'), isTrue);
|
||||
expect(agreed.agreedToSend('ja', grammarAgreementKey), isTrue);
|
||||
expect(agreed.grammarAnswerOf('ko'), isNull);
|
||||
|
||||
final declined = agreed.withGrammarAnswer('ja', check: false);
|
||||
expect(declined.grammarAnswerOf('ja'), isFalse);
|
||||
expect(
|
||||
declined.agreedToSend('ja', grammarAgreementKey),
|
||||
isFalse,
|
||||
reason: 'one answer at a time',
|
||||
);
|
||||
|
||||
expect(declined.withoutGrammarAnswer('ja').grammarAnswerOf('ja'), isNull);
|
||||
expect(agreed.withoutGrammarAnswer('ja').grammarAnswerOf('ja'), isNull);
|
||||
});
|
||||
|
||||
test('survives a round trip, in the fourteenth schema, the declined '
|
||||
'left out while there are none; a payload from before declined '
|
||||
'none', () {
|
||||
final settings = const PersonSettings()
|
||||
.withGrammarAnswer('ja', check: true)
|
||||
.withGrammarAnswer('ko', check: false)
|
||||
.withGrammarAnswer('de', check: false);
|
||||
|
||||
expect(settingsSchemaVersion, greaterThanOrEqualTo(14));
|
||||
final read = decodeSettingsPayload(encodeSettingsPayload(settings));
|
||||
expect(read, settings);
|
||||
expect(read.grammarAnswerOf('ja'), isTrue);
|
||||
expect(read.grammarAnswerOf('ko'), isFalse);
|
||||
expect(
|
||||
decodePayload(
|
||||
encodeSettingsPayload(const PersonSettings()),
|
||||
newestSchema: settingsSchemaVersion,
|
||||
).keys,
|
||||
isNot(contains('grammarDeclined')),
|
||||
);
|
||||
expect(
|
||||
decodeSettingsPayload(
|
||||
encodePayload(13, {'studyTags': <String, Object?>{}}),
|
||||
).grammarDeclined,
|
||||
isEmpty,
|
||||
);
|
||||
});
|
||||
|
||||
test("is checked, as two devices' settings fold, only where both "
|
||||
'agreed, and declined where either declined: an older row never '
|
||||
'sends what was declined since', () {
|
||||
final agreed = const PersonSettings().withGrammarAnswer(
|
||||
'ja',
|
||||
check: true,
|
||||
);
|
||||
final declined = const PersonSettings().withGrammarAnswer(
|
||||
'ja',
|
||||
check: false,
|
||||
);
|
||||
|
||||
for (final folded in [
|
||||
agreed.foldedWith(declined),
|
||||
declined.foldedWith(agreed),
|
||||
]) {
|
||||
expect(folded.grammarAnswerOf('ja'), isFalse);
|
||||
}
|
||||
expect(
|
||||
agreed.foldedWith(const PersonSettings()).grammarAnswerOf('ja'),
|
||||
isNull,
|
||||
reason: 'a row not agreeing asks again',
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,6 +261,53 @@ void main() {
|
|||
expect(store.writes.last.pomodoroWhileStudying, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('the Writing card turns off checking what is written (ADR '
|
||||
'0024), and says where the instance checks nothing yet', (tester) async {
|
||||
Future<void> show(
|
||||
List<String>? languages,
|
||||
MemoryDeviceSettingsStore store,
|
||||
) => pumpApp(
|
||||
tester,
|
||||
testDependencies(
|
||||
deviceSettingsStore: store,
|
||||
grammarLanguages: languages,
|
||||
),
|
||||
location: RoutePaths.settings,
|
||||
);
|
||||
const title = 'Check grammar while writing';
|
||||
const none =
|
||||
'This instance has no spelling and grammar check set up yet, so '
|
||||
'nothing is underlined. Left on, checking starts once it has one.';
|
||||
|
||||
final store = MemoryDeviceSettingsStore();
|
||||
await show(['en', 'de'], store);
|
||||
final toggle = find.widgetWithText(SwitchListTile, title);
|
||||
await tester.scrollUntilVisible(
|
||||
toggle,
|
||||
200,
|
||||
scrollable: find.byType(Scrollable).first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Writing'), findsOneWidget);
|
||||
expect(tester.widget<SwitchListTile>(toggle).value, isTrue);
|
||||
expect(find.text(none), findsNothing);
|
||||
|
||||
await tester.tap(
|
||||
find.descendant(of: toggle, matching: find.byType(Switch)),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(store.writes.last.checkGrammar, isFalse);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
await show(null, MemoryDeviceSettingsStore());
|
||||
await tester.scrollUntilVisible(
|
||||
find.text(none),
|
||||
200,
|
||||
scrollable: find.byType(Scrollable).first,
|
||||
);
|
||||
expect(find.text(none), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('starts with the appearance the device kept', (tester) async {
|
||||
tester.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
|
||||
addTearDown(tester.platformDispatcher.clearPlatformBrightnessTestValue);
|
||||
|
|
|
|||
Loading…
Reference in a new issue