fix(app): study options saved as they came stay the defaults
A review found three edges of the study options: - Saving a studyset's study options without changing anything wrote the defaults as its own, so it stopped following the person's new cards a day and was written in the newer schema older apps skip. It now keeps no options of its own. - The library's badges read the person's new cards a day only when something else changed; a change to the setting now refreshes them. - A person's random order shuffled the whole round, undoing the studyset's Reviews first or New first. The reviews and the new cards are now each shuffled, the order between them kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
1921043925
commit
6ba229be2c
6 changed files with 60 additions and 4 deletions
|
|
@ -122,7 +122,7 @@ class StudySessionBloc(
|
|||
for (final group in groups) [for (final card in group) card.id],
|
||||
];
|
||||
final queue = _queue(schedules, now);
|
||||
_round = _ordered(queue).take(_options.cardsPerRound).toList();
|
||||
_round = queue.take(_options.cardsPerRound).toList();
|
||||
if (_round.isEmpty) {
|
||||
final upcoming = [
|
||||
for (final schedule in schedules.values) ?schedule.due,
|
||||
|
|
@ -140,6 +140,9 @@ class StudySessionBloc(
|
|||
groups: _groupIds,
|
||||
options: _groupOptions,
|
||||
order: _order,
|
||||
// Shuffled within reviews and new cards, so that the studyset's
|
||||
// order between them holds.
|
||||
shuffle: _options.effectiveOrder == StudyOrder.random ? _random : null,
|
||||
schedules: schedules,
|
||||
now: now,
|
||||
startOfToday: startOfLocalDay(now),
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ import 'package:halkyon_learn_flutter/src/feature/study/model/studyset_study_opt
|
|||
/// most each studyset's new cards a day. A day is the local one that began at
|
||||
/// [startOfToday], and what was reviewed or started in it counts. [order]
|
||||
/// (the studied studyset's) says whether the due cards or the new ones come
|
||||
/// first, or the new ones spread among the due. When nothing else is due, a
|
||||
/// learning card due within [learnAhead] comes early.
|
||||
/// first, or the new ones spread among the due; with [shuffle], the due
|
||||
/// cards and the new ones are each shuffled first, keeping that order between
|
||||
/// them. When nothing else is due, a learning card due within [learnAhead]
|
||||
/// comes early.
|
||||
List<String> studyQueue({
|
||||
required List<List<String>> groups,
|
||||
required Map<String, CardSchedule> schedules,
|
||||
|
|
@ -26,6 +28,7 @@ List<String> studyQueue({
|
|||
List<StudysetStudyOptions> options = const [],
|
||||
int newPerDay = 20,
|
||||
NewCardOrder order = NewCardOrder.reviewsFirst,
|
||||
math.Random? shuffle,
|
||||
Duration learnAhead = const Duration(minutes: 20),
|
||||
}) {
|
||||
DateTime? dueOf(String id) => schedules[id]?.due;
|
||||
|
|
@ -68,6 +71,10 @@ List<String> studyQueue({
|
|||
);
|
||||
}
|
||||
due.sort(byDue);
|
||||
if (shuffle != null) {
|
||||
due.shuffle(shuffle);
|
||||
fresh.shuffle(shuffle);
|
||||
}
|
||||
final queue = switch (order) {
|
||||
NewCardOrder.reviewsFirst => [...due, ...fresh],
|
||||
NewCardOrder.newFirst => [...fresh, ...due],
|
||||
|
|
|
|||
|
|
@ -64,7 +64,13 @@ class _StudyOptionsSheetState() extends State<_StudyOptionsSheet> {
|
|||
),
|
||||
const Spacer(),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop((options: _options)),
|
||||
// Saved as they came, the defaults stay the defaults: the
|
||||
// studyset keeps following the person's study settings.
|
||||
onPressed: () => Navigator.of(context).pop((
|
||||
options: widget.current == null && _options == widget.defaults
|
||||
? null
|
||||
: _options,
|
||||
)),
|
||||
child: Text(l10n.commonSave),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -277,6 +277,13 @@ class StudysetListBloc(
|
|||
_paths.changes,
|
||||
onData: (_) => add(const StudysetListEventProgressRefreshRequested()),
|
||||
),
|
||||
// So do the person's new cards a day, which studysets without study
|
||||
// options of their own follow.
|
||||
emit.onEach(
|
||||
_settings.watch(),
|
||||
onData: (_) => add(const StudysetListEventProgressRefreshRequested()),
|
||||
onError: (_, _) {},
|
||||
),
|
||||
emit.onEach(
|
||||
_deletions.watchPending(),
|
||||
onData: (pending) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/model/card_schedule.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/model/study_queue.dart';
|
||||
|
|
@ -196,6 +198,24 @@ void main() {
|
|||
expect(queue, ['n1', 'n2', 'due']);
|
||||
});
|
||||
|
||||
test('shuffled, the reviews still come before the new cards', () {
|
||||
final queue = studyQueue(
|
||||
groups: [
|
||||
['d1', 'd2', 'd3', 'n1', 'n2', 'n3'],
|
||||
],
|
||||
shuffle: math.Random(7),
|
||||
schedules: {
|
||||
for (final (i, id) in ['d1', 'd2', 'd3'].indexed)
|
||||
id: review(overdue.add(Duration(minutes: i))),
|
||||
},
|
||||
now: now,
|
||||
startOfToday: startOfToday,
|
||||
);
|
||||
|
||||
expect(queue.take(3).toSet(), {'d1', 'd2', 'd3'});
|
||||
expect(queue.skip(3).toSet(), {'n1', 'n2', 'n3'});
|
||||
});
|
||||
|
||||
test('mixed spreads the new cards evenly among the due ones', () {
|
||||
final queue = studyQueue(
|
||||
groups: [
|
||||
|
|
|
|||
|
|
@ -425,6 +425,19 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets('saved as they came, the defaults stay the defaults', (
|
||||
tester,
|
||||
) async {
|
||||
stored('japanese', 'Japanese');
|
||||
await showLibrary(tester);
|
||||
|
||||
await choose(tester, 'Japanese', 'Study options');
|
||||
await tester.tap(find.text('Save'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(gateway.writes, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('go back to the defaults with Reset', (tester) async {
|
||||
gateway.receiveRemoteChange(
|
||||
'japanese',
|
||||
|
|
|
|||
Loading…
Reference in a new issue