Trigger ContactDetailService refresh after successful save
epic-contact-detail-and-edit-service-layer-task-008 — After a successful partial update in ContactEditService, trigger a refresh of the ContactDetailService state stream so the detail screen reflects the latest server-side data without requiring a manual reload by the user. Ensure both services coordinate via the shared Riverpod container or BLoC event bus without tight coupling.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Preferred pattern with Riverpod: after ContactEditService confirms success, call ref.invalidate(contactDetailProvider(contactId)). This decouples the services — ContactEditService only needs access to the Riverpod Ref, not a reference to ContactDetailService. With BLoC: dispatch a ContactRefreshRequested event to the ContactDetailBloc via the shared event bus or by injecting the bloc's Sink (not the full bloc). Avoid passing the updated Contact object directly from edit to detail — always re-fetch from Supabase to guarantee server-side state (computed fields, updated_at, etc.
may differ). Add a brief shimmer or spinner overlay to the detail screen during the re-fetch to signal freshness without a full rebuild. Guard against race conditions: if the user triggers a second save before the first re-fetch completes, cancel the in-flight re-fetch and restart.
Testing Requirements
Write integration tests using flutter_test and a mocked Supabase client: (1) verify ContactDetailService.state transitions to ContactDetailLoading then ContactDetailLoaded after a ContactEditSuccess event is emitted, (2) verify the loaded contact contains the updated field values, (3) verify that no re-fetch is triggered when ContactEditService emits ContactEditError, (4) verify re-fetch completes without error even when the ContactDetail widget is not mounted. Unit test the coordination mechanism in isolation to confirm it does not create a circular dependency.
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.