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>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-17 01:21:16 +02:00
commit 301751607d
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -20,20 +20,23 @@ class FakeReviewGateway() implements ReviewGateway {
Future<List<ReviewRow>> allReviews() async => [...rows];
@override
Future<void> insert(Iterable<ReviewRow> reviews) async {
Future<List<ReviewRow>> insert(Iterable<ReviewRow> reviews) async {
if (failWrites case final error?) {
throw error;
}
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,
));
}
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