feat: notes in studysets, synced and merged as cards are, with tags
Server: three synced tables, only additions (migration
20261002191122122-notes): `note` (its studyset, a fractional position),
`note_revision` (append-only, as a card's: parents, author, report,
the payload {schemaVersion, title, body}) and `note_tag` (one row per
assignment, never updated, as `card_tag`). Each indexes `spaceId`, and
everything goes with its space, studyset, note or tag. A migration of
synced tables locks app builds from before it out of syncing (ADR 0002,
rule 3): cheap now, before the first release.
App, `feature/note`:
- `NoteContent` is a title and rich text, merged apart (ADR 0003): a
title changed on one device and the body on another both survive, a
body changed on two is a conflict.
- `NoteRepository` creates a note at the end of its studyset, saves an
edit on the revisions it started from (`saveEdit`, as the card
composer now does), says when a note was last edited (its newest
head's UUID v7), counts notes per studyset, and deletes a note's
revisions blanked first (ADR 0002, rule 2).
- Notes share the person's tags: `setTagsOfNote`, and folding tags of
one name or undoing a tag's deletion covers notes too.
- The delayed deletion with its Undo moves to `core/async`
(`DelayedDeletions`): `StudysetDeletions` builds on it and now takes
a studyset's notes too; `NoteDeletions` deletes a single note.
Tests: content, merge, payload, repository, deletions, tags, the device
migration guard, and sync tests against the server (four).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
9639ce8569
commit
f3b56f2c0c
1 changed files with 25469 additions and 131 deletions
|
|
@ -9,6 +9,9 @@ import 'package:halkyon_learn_flutter/src/core/sync/sync_service.dart';
|
|||
import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/data/sync_card_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/model/card.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/data/sync_note_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/note/model/note.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/data/sync_settings_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/data/review_repository.dart';
|
||||
|
|
@ -121,6 +124,9 @@ final class SyncDevice(
|
|||
/// The app's cards on this device's database.
|
||||
final CardRepository cards,
|
||||
|
||||
/// The app's notes on this device's database.
|
||||
final NoteRepository notes,
|
||||
|
||||
/// The app's reviews on this device's database.
|
||||
final ReviewRepository reviews,
|
||||
|
||||
|
|
@ -154,16 +160,21 @@ final class SyncDevice(
|
|||
final cards = CardRepository(
|
||||
SyncCardGateway(db, syncRequests, person.authUserId.uuid),
|
||||
);
|
||||
final notes = NoteRepository(
|
||||
SyncNoteGateway(db, syncRequests, person.authUserId.uuid),
|
||||
);
|
||||
return SyncDevice(
|
||||
client,
|
||||
db,
|
||||
syncRequests,
|
||||
StudysetRepository(SyncStudysetGateway(db, syncRequests)),
|
||||
cards,
|
||||
notes,
|
||||
ReviewRepository(SyncReviewGateway(db, syncRequests)),
|
||||
TagRepository(
|
||||
SyncTagGateway(db, syncRequests),
|
||||
cardChanges: cards.changes,
|
||||
noteChanges: notes.changes,
|
||||
),
|
||||
SettingsRepository(SyncSettingsGateway(db, syncRequests)),
|
||||
AssetRepository(
|
||||
|
|
@ -197,6 +208,16 @@ final class SyncDevice(
|
|||
return null;
|
||||
}
|
||||
|
||||
/// The note with [id] in [studysetId] on this device, or null.
|
||||
Future<Note?> note(String studysetId, String id) async {
|
||||
for (final note in await notes.notesOf(studysetId)) {
|
||||
if (note.id == id) {
|
||||
return note;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Closes everything the device opened.
|
||||
Future<void> close() async {
|
||||
await studysets.dispose();
|
||||
|
|
@ -204,6 +225,7 @@ final class SyncDevice(
|
|||
await settings.dispose();
|
||||
await reviews.dispose();
|
||||
await cards.dispose();
|
||||
await notes.dispose();
|
||||
await assets.dispose();
|
||||
await _syncRequests.dispose();
|
||||
await _db.close();
|
||||
|
|
|
|||
Loading…
Reference in a new issue