Expose receipt confirmation status for UI feedback
epic-contact-detail-and-edit-service-layer-task-011 — After a successful write in ReadReceiptService, update the service state to ReadReceiptConfirmed carrying the field key so the EncryptedFieldDisplay widget can show a visual confirmation (e.g., a tick or timestamp label) without polling. Ensure the confirmation state is scoped per field key so multiple fields can be confirmed independently.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
To support per-field-key state independence, change the provider family key from String contactId to a record type (String contactId, String fieldKey): final readReceiptServiceProvider = StateNotifierProvider.family
The confirmation UI element (tick icon or 'Sett' label with timestamp) should use the design token color system for the confirmed visual state.
Testing Requirements
Write unit tests: (1) confirm that after recordFieldReveal('contact-1', 'ssn') the state for fieldKey 'ssn' is ReadReceiptConfirmed and the state for fieldKey 'phone' remains ReadReceiptIdle, (2) confirm that rapid successive reveals of different field keys each produce distinct Confirmed states, (3) confirm that a widget using select() to watch isConfirmed('ssn') is not notified when 'phone' is confirmed, (4) confirm that calling recordFieldReveal for an already-confirmed field re-enters Writing then Confirmed (creates a new audit record). Write a widget test confirming EncryptedFieldDisplay shows the tick/timestamp only after the Confirmed state is emitted for its specific fieldKey.
Parallel fetching of profile, activity history, and assignment status from contact-detail-service may produce race conditions where partial state is emitted to the UI before all fetches complete, resulting in flickering or incorrect loading indicators.
Mitigation & Contingency
Mitigation: Use Future.wait or a single composed BLoC event that only emits a loaded state once all three futures resolve. Define a strict state machine: initial → loading → loaded/error with no intermediate partial-loaded states emitted to the UI.
Contingency: If parallelism proves unreliable in testing, fall back to sequential fetching with a combined loading indicator. The 500ms target may need to be renegotiated with stakeholders if sequential fetching exceeds it on slow connections.
The partial-field update pattern in contact-edit-service assumes the contact record has not changed between when the edit screen was loaded and when the save is submitted. Concurrent edits by another coordinator could cause the earlier editor's save to silently overwrite the later one.
Mitigation & Contingency
Mitigation: Include an updated_at timestamp in the PATCH request and configure Supabase to reject updates where the server-side timestamp differs from the client's version. Return a 409-equivalent error that the service maps to a user-readable conflict message.
Contingency: If optimistic locking is too complex for initial delivery, implement a simple 'reload and retry' flow: on save error, reload the contact detail and prompt the coordinator to re-apply their changes manually.