feat(learn): a studyset is made private in Edit, no longer from its options

The developer's review of 3 Nov 2026: Make private and Make non-private
leave a studyset's options (its card's menu, its screen's); a top-level
studyset's Edit has a Visibility section instead, its switch making it
private, or non-private, at once once the person agreed in the dialog as
before (ADR 0019), not on Save, and what that means behind the question
mark. A topic's Edit offers none, its privacy being its tree's. The lock on
a studyset's card still explains its privacy and keeps its own button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 21:50:00 +01:00
commit a7904bac05
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 156 additions and 61 deletions

View file

@ -482,6 +482,42 @@ void main() {
expect(tops, [...tops]..sort());
});
testWidgets('Edit makes a top-level studyset private once agreed, as its '
"options no longer do (the developer's review of 3 Nov 2026)", (
tester,
) async {
stored('japanese', 'Japanese');
gateway.canMakePrivate = true;
await showLibrary(tester);
await tester.tap(onCard('Japanese', find.byTooltip('Options')));
await tester.pumpAndSettle();
expect(find.text('Make private'), findsNothing);
await tester.tap(find.text('Edit'));
await tester.pumpAndSettle();
final private = find.widgetWithText(SwitchListTile, 'Private');
await tester.scrollUntilVisible(
private,
200,
scrollable: find.byType(Scrollable).last,
);
expect(tester.widget<SwitchListTile>(private).value, isFalse);
await tester.tap(private);
await tester.pumpAndSettle();
expect(find.text('Make “Japanese” private?'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, 'Make private'));
await tester.pumpAndSettle();
expect(gateway.privacyChanges, [('japanese', true)]);
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(tester.widget<SwitchListTile>(private).value, isTrue);
});
testWidgets('moves a studyset into another from its options', (
tester,
) async {

View file

@ -14,41 +14,27 @@ void main() {
for (final entry in studysetOptions(l10n, studyset)) entry.option,
};
test('a readable top-level studyset can be made private, a private one '
'non-private', () {
final readable = Studyset(
id: 's',
content: StudysetContent.titled('Diary'),
);
final private = Studyset(
id: 's',
content: StudysetContent.titled('Diary'),
isPrivate: true,
);
expect(optionsOf(readable), contains(StudysetOption.makePrivate));
expect(optionsOf(readable), isNot(contains(StudysetOption.makeReadable)));
expect(optionsOf(private), contains(StudysetOption.makeReadable));
expect(optionsOf(private), isNot(contains(StudysetOption.makePrivate)));
});
test("a topic's privacy is its tree's: it offers neither", () {
final topic = Studyset(
id: 't',
content: StudysetContent.titled('Kanji'),
parentId: 's',
isPrivate: true,
);
expect(
optionsOf(topic),
isNot(
anyOf(
contains(StudysetOption.makePrivate),
contains(StudysetOption.makeReadable),
),
test("no studyset's options make it private or non-private: that is "
"done in Edit (the developer's review of 3 Nov 2026)", () {
for (final studyset in [
Studyset(id: 's', content: StudysetContent.titled('Diary')),
Studyset(
id: 's',
content: StudysetContent.titled('Diary'),
isPrivate: true,
),
);
Studyset(
id: 't',
content: StudysetContent.titled('Kanji'),
parentId: 's',
isPrivate: true,
),
]) {
expect(
[for (final entry in studysetOptions(l10n, studyset)) entry.label],
isNot(anyOf(contains('Make private'), contains('Make non-private'))),
);
}
});
test('a locked studyset offers nothing but deleting', () {