high priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

ContactEditService.save(ContactEditPayload) method exists and is callable from Riverpod providers or BLoC
Calling save immediately updates the local contact state (optimistic update) before awaiting the Supabase response
On successful Supabase response, the contact-detail-service cache for the affected contactId is invalidated/refreshed
On Supabase network error or non-2xx response, the optimistic update is reverted and the previous state is restored
On failure, save throws or returns a typed ContactEditError (subtypes: NetworkError, ValidationError, PermissionError) — never a raw Exception
ContactEditPayload only reaches the repository if it passes a non-null, non-empty contactId check
Service is covered by unit tests using a mock contact-detail-repository — no real Supabase calls in unit tests
Optimistic update is applied within the same synchronous frame as the save call (before first await)

Technical Requirements

frameworks
Flutter
Riverpod
Supabase
apis
Supabase REST API — contacts table update (PATCH /rest/v1/contacts?id=eq.{id})
data models
Contact
ContactEditPayload
ContactEditError
performance requirements
Optimistic update must be applied before the first network round-trip to prevent UI lag
Cache invalidation must not trigger a full page reload — only the affected contact record should be refreshed
security requirements
Supabase RLS policies must be respected — service must not bypass row-level security
ContactEditPayload must be serialised without including sensitive fields not intended for update (e.g. encrypted assignment data)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement the optimistic update by capturing the current state snapshot before the first `await`, applying the new state immediately, then in a try/catch reverting to the snapshot on error. Use Riverpod StateNotifier or AsyncNotifier for the contact state. To invalidate the cache, call `ref.invalidate(contactDetailProvider(contactId))` on success. Define ContactEditError as a sealed class so callers are forced to handle all error variants exhaustively.

Coordinate with the contact-detail-repository interface to ensure the `update` method signature accepts a ContactEditPayload and returns the updated Contact or throws a typed error. Avoid putting business logic (validation) in this service — validation is the responsibility of contact-form-validator (task-012).

Testing Requirements

Unit tests with mock repository (implement ContactDetailRepository as an abstract interface and inject a fake in tests). Test scenarios: (1) successful save — assert optimistic state applied, repository.update called once, cache invalidated; (2) network failure — assert state reverted to pre-save snapshot, ContactEditError.networkError returned; (3) permission error (403 from Supabase) — assert ContactEditError.permissionError returned; (4) empty contactId — assert ContactEditError.validationError without calling repository. Use Riverpod's ProviderContainer with overrides for isolation. No real async timers needed.

Component
Edit Contact Screen
ui medium
Epic Risks (3)
medium impact medium prob scope

The encrypted-field-display confirmation dialog adds interaction steps that may frustrate coordinators who access sensitive fields frequently, leading to requests to bypass the flow or skip read-receipt logging, which would violate Blindeforbundet's compliance requirements.

Mitigation & Contingency

Mitigation: Design the confirmation dialog to be as minimal as possible (one clear sentence, single confirm action) and ensure it does not reappear for the same field within a single screen session. Validate the UX with Blindeforbundet coordinators during the TestFlight pilot before finalising.

Contingency: If coordinators raise strong objections, escalate to Blindeforbundet's data protection officer to determine whether a lighter confirmation pattern (e.g., biometric confirmation instead of dialog) satisfies their compliance obligation.

medium impact medium prob technical

The activity-history-list infinite scroll requires paginated Supabase queries. Contacts with hundreds of activities (e.g., an HLF peer mentor with 380 annual registrations) could cause slow page loads or memory pressure on older devices if pagination boundaries are set too large.

Mitigation & Contingency

Mitigation: Use a page size of 20 records with cursor-based pagination. Implement list item recycling using Flutter's ListView.builder. Benchmark memory usage with 400+ item simulation on a low-end test device before TestFlight release.

Contingency: If performance degrades on older devices, reduce page size to 10 and add a time-window filter (last 30 days, last 6 months, all) so the default view loads a manageable record count for most coordinators.

low impact high prob scope

The cognitive load rule engine (from the Cognitive Accessibility feature) mandates no more than 7 fields per screen section. If a contact model has more than 7 editable fields, the edit-contact-screen layout must be split into sections, adding complexity not accounted for in the initial scope.

Mitigation & Contingency

Mitigation: Audit the full contact field list from all four organisations before implementation. Group fields into logical sections (personal info, contact details, affiliation) so no single section exceeds 7 fields. Use the cognitive-load-rule-engine component if it is already delivered by the Cognitive Accessibility feature.

Contingency: If the rule-engine component is not yet available, implement a simple manual section layout with accordion-style expansion for less-frequently edited fields to stay within the 7-field guideline without blocking delivery.