feat(app): study with FSRS
A studyset's card list gains "Study". The study screen shows the front, then the answer and the four ratings, each labelled with when the card would come back; every rating is recorded with how long answering took, and the queue is worked out anew after each.
Schedules are never stored: scheduleOf replays a card's reviews in time order with FSRS-6 (fsrs v2.0.1, pinned exactly so every device on one app version computes the same). The package's fuzz can only be seeded through an API meant for tests, and due dates don't feed back into a card's state, so the replay runs without fuzz and fuzzedDays spreads the final due date over FSRS's range, picked by a stable FNV-1a fraction of the card's id and review count. studyQueue takes due cards earliest first, then new cards in studyset order, 20 per studyset and local day, and brings a learning card due within 20 minutes early when nothing else is due. Deleting a card deletes its reviews.
Tests: the replay (learning, growing intervals, the same schedule whatever order reviews arrive in, previews matching what a rating yields), the fuzz range, the queue (order, the daily limit counted per local day, learn-ahead), the repository, the session bloc under a controlled clock, the screen, and a sync test for ratings on two devices. AGENTS.md records the review log and scheduling, and how plugin lint rules are ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 16:59:07 +02:00
|
|
|
import 'package:halkyon_learn_flutter/src/feature/study/data/review_gateway.dart';
|
|
|
|
|
|
|
|
|
|
/// A [ReviewGateway] in memory; ids sort by creation, as UUID v7 ids do.
|
|
|
|
|
class FakeReviewGateway() implements ReviewGateway {
|
|
|
|
|
var _nextId = 1;
|
|
|
|
|
|
|
|
|
|
/// Every review stored, in order.
|
|
|
|
|
final rows = <ReviewRow>[];
|
|
|
|
|
|
2026-10-07 21:00:42 +02:00
|
|
|
/// When set, storing a review fails with it.
|
|
|
|
|
Exception? failWrites;
|
|
|
|
|
|
feat(app): study with FSRS
A studyset's card list gains "Study". The study screen shows the front, then the answer and the four ratings, each labelled with when the card would come back; every rating is recorded with how long answering took, and the queue is worked out anew after each.
Schedules are never stored: scheduleOf replays a card's reviews in time order with FSRS-6 (fsrs v2.0.1, pinned exactly so every device on one app version computes the same). The package's fuzz can only be seeded through an API meant for tests, and due dates don't feed back into a card's state, so the replay runs without fuzz and fuzzedDays spreads the final due date over FSRS's range, picked by a stable FNV-1a fraction of the card's id and review count. studyQueue takes due cards earliest first, then new cards in studyset order, 20 per studyset and local day, and brings a learning card due within 20 minutes early when nothing else is due. Deleting a card deletes its reviews.
Tests: the replay (learning, growing intervals, the same schedule whatever order reviews arrive in, previews matching what a rating yields), the fuzz range, the queue (order, the daily limit counted per local day, learn-ahead), the repository, the session bloc under a controlled clock, the screen, and a sync test for ratings on two devices. AGENTS.md records the review log and scheduling, and how plugin lint rules are ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 16:59:07 +02:00
|
|
|
@override
|
|
|
|
|
Future<List<ReviewRow>> reviewsOf(Set<String> cardIds) async => [
|
|
|
|
|
for (final row in rows)
|
|
|
|
|
if (cardIds.contains(row.cardId)) row,
|
|
|
|
|
];
|
|
|
|
|
|
2026-10-01 21:59:52 +02:00
|
|
|
@override
|
|
|
|
|
Future<List<ReviewRow>> allReviews() async => [...rows];
|
|
|
|
|
|
feat(app): study with FSRS
A studyset's card list gains "Study". The study screen shows the front, then the answer and the four ratings, each labelled with when the card would come back; every rating is recorded with how long answering took, and the queue is worked out anew after each.
Schedules are never stored: scheduleOf replays a card's reviews in time order with FSRS-6 (fsrs v2.0.1, pinned exactly so every device on one app version computes the same). The package's fuzz can only be seeded through an API meant for tests, and due dates don't feed back into a card's state, so the replay runs without fuzz and fuzzedDays spreads the final due date over FSRS's range, picked by a stable FNV-1a fraction of the card's id and review count. studyQueue takes due cards earliest first, then new cards in studyset order, 20 per studyset and local day, and brings a learning card due within 20 minutes early when nothing else is due. Deleting a card deletes its reviews.
Tests: the replay (learning, growing intervals, the same schedule whatever order reviews arrive in, previews matching what a rating yields), the fuzz range, the queue (order, the daily limit counted per local day, learn-ahead), the repository, the session bloc under a controlled clock, the screen, and a sync test for ratings on two devices. AGENTS.md records the review log and scheduling, and how plugin lint rules are ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 16:59:07 +02:00
|
|
|
@override
|
2026-10-01 19:03:31 +02:00
|
|
|
Future<void> insert(Iterable<ReviewRow> reviews) async {
|
2026-10-07 21:00:42 +02:00
|
|
|
if (failWrites case final error?) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2026-10-01 19:03:31 +02:00
|
|
|
for (final review in reviews) {
|
|
|
|
|
rows.add((
|
|
|
|
|
id: 'review-${(_nextId++).toString().padLeft(4, '0')}',
|
|
|
|
|
cardId: review.cardId,
|
|
|
|
|
rating: review.rating,
|
|
|
|
|
reviewedAt: review.reviewedAt,
|
|
|
|
|
durationMs: review.durationMs,
|
|
|
|
|
mode: review.mode,
|
|
|
|
|
));
|
|
|
|
|
}
|
feat(app): study with FSRS
A studyset's card list gains "Study". The study screen shows the front, then the answer and the four ratings, each labelled with when the card would come back; every rating is recorded with how long answering took, and the queue is worked out anew after each.
Schedules are never stored: scheduleOf replays a card's reviews in time order with FSRS-6 (fsrs v2.0.1, pinned exactly so every device on one app version computes the same). The package's fuzz can only be seeded through an API meant for tests, and due dates don't feed back into a card's state, so the replay runs without fuzz and fuzzedDays spreads the final due date over FSRS's range, picked by a stable FNV-1a fraction of the card's id and review count. studyQueue takes due cards earliest first, then new cards in studyset order, 20 per studyset and local day, and brings a learning card due within 20 minutes early when nothing else is due. Deleting a card deletes its reviews.
Tests: the replay (learning, growing intervals, the same schedule whatever order reviews arrive in, previews matching what a rating yields), the fuzz range, the queue (order, the daily limit counted per local day, learn-ahead), the repository, the session bloc under a controlled clock, the screen, and a sync test for ratings on two devices. AGENTS.md records the review log and scheduling, and how plugin lint rules are ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 16:59:07 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-01 19:34:44 +02:00
|
|
|
@override
|
2026-10-07 21:06:58 +02:00
|
|
|
Future<void> deleteReviews(
|
|
|
|
|
Set<String> cardIds,
|
|
|
|
|
DateTime at,
|
|
|
|
|
String mode,
|
|
|
|
|
) async => rows.removeWhere(
|
|
|
|
|
(row) =>
|
|
|
|
|
cardIds.contains(row.cardId) &&
|
|
|
|
|
row.mode == mode &&
|
|
|
|
|
row.reviewedAt == at,
|
|
|
|
|
);
|
2026-10-01 19:34:44 +02:00
|
|
|
|
feat(app): study with FSRS
A studyset's card list gains "Study". The study screen shows the front, then the answer and the four ratings, each labelled with when the card would come back; every rating is recorded with how long answering took, and the queue is worked out anew after each.
Schedules are never stored: scheduleOf replays a card's reviews in time order with FSRS-6 (fsrs v2.0.1, pinned exactly so every device on one app version computes the same). The package's fuzz can only be seeded through an API meant for tests, and due dates don't feed back into a card's state, so the replay runs without fuzz and fuzzedDays spreads the final due date over FSRS's range, picked by a stable FNV-1a fraction of the card's id and review count. studyQueue takes due cards earliest first, then new cards in studyset order, 20 per studyset and local day, and brings a learning card due within 20 minutes early when nothing else is due. Deleting a card deletes its reviews.
Tests: the replay (learning, growing intervals, the same schedule whatever order reviews arrive in, previews matching what a rating yields), the fuzz range, the queue (order, the daily limit counted per local day, learn-ahead), the repository, the session bloc under a controlled clock, the screen, and a sync test for ratings on two devices. AGENTS.md records the review log and scheduling, and how plugin lint rules are ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 16:59:07 +02:00
|
|
|
@override
|
|
|
|
|
Stream<void> get remoteChanges => const Stream.empty();
|
|
|
|
|
}
|