halkyon/halkyon_learn_flutter/test/support/fake_review_gateway.dart
Tizian 「ティツィアーン」 301751607d
perf: counts worked out once and kept until something changes
On a copy of a real library (8,067 cards, 20,907 reviews, 20
studysets), every open list read every card (0.8 s), every review
(0.25 s) and replayed schedules after every change, each on its own:
the library, every studyset open on the stack and its topics.

- CardRepository keeps each studyset's cards and every card's stamp
  until a card changes; a read begun before a change keeps nothing.
- ReviewRepository keeps the whole log once many cards' reviews are
  asked for, updated by this device's writes and read anew after a
  sync brings others'.
- StudysetProgressRepository works out every studyset's progress once
  for every list and hands the last to whoever follows it next;
  StudysetListBloc reads it.

A full recount now takes about 65 ms. Home no longer rereads every
revision after each rating.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-17 19:46:35 +02:00

56 lines
1.4 KiB
Dart

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>[];
/// When set, storing a review fails with it.
Exception? failWrites;
@override
Future<List<ReviewRow>> reviewsOf(Set<String> cardIds) async => [
for (final row in rows)
if (cardIds.contains(row.cardId)) row,
];
@override
Future<List<ReviewRow>> allReviews() async => [...rows];
@override
Future<List<ReviewRow>> insert(Iterable<ReviewRow> reviews) async {
if (failWrites case final error?) {
throw error;
}
final stored = [
for (final review in reviews)
(
id: 'review-${(_nextId++).toString().padLeft(4, '0')}',
cardId: review.cardId,
rating: review.rating,
reviewedAt: review.reviewedAt,
durationMs: review.durationMs,
mode: review.mode,
),
];
rows.addAll(stored);
return stored;
}
@override
Future<void> deleteReviews(
Set<String> cardIds,
DateTime at,
String mode,
) async => rows.removeWhere(
(row) =>
cardIds.contains(row.cardId) &&
row.mode == mode &&
row.reviewedAt == at,
);
@override
Stream<void> get remoteChanges => const Stream.empty();
}