enrolInStarter method
- String domainId
Adds a curated starter to the learner's library. Nothing is copied — the enrolment is a reference, so progress is recorded against the shared content and fixes to the curriculum reach every learner.
Idempotent: re-enrolling is a no-op rather than a primary-key error.
Implementation
Future<void> enrolInStarter(String domainId) async {
final userId = _client.auth.currentUser?.id;
if (userId == null) {
throw StateError('Cannot enrol in a starter: user is not authenticated');
}
// `ignoreDuplicates` makes this ON CONFLICT DO NOTHING rather than an
// UPDATE. That matters: migration 072 deliberately grants no UPDATE policy
// on this table ("the row is a membership fact with nothing mutable on
// it"), so a merge-style upsert took the UPDATE path on the second call and
// failed RLS with 42501 — i.e. tapping "add to my library" twice threw.
// Re-enrolling is a genuine no-op, so DO NOTHING is also the honest
// semantics.
await _client
.from('user_starter_enrollments')
.upsert(
{'user_id': userId, 'domain_id': domainId},
onConflict: 'user_id,domain_id',
ignoreDuplicates: true,
);
}