feat(app): the card composer's state: many cards, saved on leaving

A bloc holding a studyset's cards as drafts, new ones on top and the
stored ones below. A card is saved when the person leaves it, once it
changed and has everything its type asks for, only its type's fields
stored; a failed save waits for the next leaving. A new card takes the
type of the card before it and what is pinned of it (front, answer,
tags); duplicating, clearing and deleting; a card devices changed
differently first needs a version chosen for every field in conflict.

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-01 18:21:54 +02:00
commit a5515c13f7
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -19,6 +19,9 @@ class FakeCardGateway() implements CardGateway {
/// The deletion steps, in order: `blank <card id>`, `delete <card id>`.
final deletions = <String>[];
/// When set, storing a card or a revision fails with it.
Exception? failWrites;
/// The payload each revision holds now, by revision id.
Map<String, ByteData> get payloads => {
for (final revision in _revisions.values) revision.id: revision.payload,
@ -59,6 +62,9 @@ class FakeCardGateway() implements CardGateway {
required String position,
required ByteData payload,
}) async {
if (failWrites case final error?) {
throw error;
}
final id = _newId('card');
_cards[id] = (studysetId: studysetId, position: position);
await insertRevision(cardId: id, parents: const {}, payload: payload);
@ -71,6 +77,9 @@ class FakeCardGateway() implements CardGateway {
required Set<String> parents,
required ByteData payload,
}) async {
if (failWrites case final error?) {
throw error;
}
final revision = (
id: _newId('rev'),
cardId: cardId,