Implement State Invalidation and Reload After Mutations
epic-certification-management-automation-task-008 — After RecordRenewal or EnrolInCourse success, invalidate the cached certification state and re-dispatch LoadCertification to ensure the UI reflects the updated data. Implement a reload strategy that avoids redundant network calls when the data has not changed. Expose a refresh method for pull-to-refresh triggers from UI screens.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
The cleanest pattern: inside the `RecordRenewal` handler, after emitting `RenewalSuccess`, immediately call `add(LoadCertification(certificationId: event.certificationId))`. This keeps the reload within the BLoC's own event queue rather than using a raw `await _handleLoad(...)` call, which would bypass the transformer and concurrency guard. For the equality-check optimisation, add a guard at the start of the `LoadCertification` handler: `if (state is CertificationLoaded && (state as CertificationLoaded).certification == fetchedCert) return;` before emitting. Expose `refresh` as a thin wrapper: `void refresh(String id) => add(LoadCertification(certificationId: id));` — no need for a separate event class unless refresh needs distinct UI behaviour (e.g.
showing a different shimmer). Document the chosen loading-state strategy (hold success vs show loading during reload) with a one-line comment so future developers understand the intentional UX decision.
Testing Requirements
Extend the unit test suite from task-007. Test 1 (auto-reload after RenewalSuccess): after emitting RenewalSuccess, assert that the BLoC subsequently emits CertificationLoaded with the refreshed data — verify service.getCertification was called exactly twice (once for initial load, once for reload). Test 2 (no duplicate emission when data unchanged): mock service to return the same Certification before and after reload; assert only one CertificationLoaded emission occurs. Test 3 (refresh method): call `bloc.refresh(id)` directly; assert states `[CertificationLoading, CertificationLoaded]`.
Test 4 (concurrent refresh drop): call refresh twice rapidly with droppable transformer; assert getCertification called only once. Test 5 (auto-reload after EnrolmentSuccess): mirrors test 1 for the enrolment mutation path.
Supabase Edge Functions can have cold-start latency that causes the nightly cron to time out when processing large cohorts of expiring certifications, resulting in partial reminder dispatches.
Mitigation & Contingency
Mitigation: Batch the cron processing in chunks of 50 mentors per iteration. Use pagination with a cursor to resume processing if the function is re-invoked. Keep total invocation time well under the Edge Function timeout limit.
Contingency: If timeouts occur in production, split the cron into two separate functions: one for reminders and one for auto-pauses, each with its own schedule offset to reduce peak load.
Certification BLoC covers three distinct workflows (view, renew, enrol) which may lead to an overly complex state machine that is hard to test and maintain, particularly when error states from multiple concurrent operations need to be differentiated in the UI.
Mitigation & Contingency
Mitigation: Use separate sealed state classes per workflow (CertificationViewState, RenewalState, EnrolmentState) composed into a single BLoC state wrapper. Follow the existing BLoC patterns established in the codebase for consistency.
Contingency: If the BLoC grows too complex, split into two BLoCs: CertificationBLoC (view/load) and CertificationActionBLoC (mutations), connected via a shared stream.