Implement offline cache for mentor location data
epic-geographic-peer-mentor-map-core-services-task-008 — Add an offline caching layer to MentorLocationService using a local Hive or shared_preferences store. On successful fetch, persist the mentor location list with a TTL of 24 hours. On network failure, serve the cached result and emit a CacheHitState so the UI can display a 'last updated' indicator. This ensures Norwegian coordinators in rural areas with poor connectivity still access mentor data. Include eviction logic and a clearCache() method.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Prefer Hive over shared_preferences for this use case — Hive supports typed adapters and is faster for list serialisation. Create a MentorLocationCacheAdapter (HiveObject) that stores the JSON-encoded list and an ISO-8601 timestamp string. Alternatively, if Hive is not already a project dependency, use shared_preferences with JSON.encode/decode and store the timestamp as a separate key (mentor_locations_v1_{orgId}_data and mentor_locations_v1_{orgId}_ts). Abstract the storage behind a MentorLocationCacheRepository interface so the concrete implementation can be swapped in tests.
The 24-hour TTL should be a configurable constant (kMentorLocationCacheTtl = const Duration(hours: 24)) in a constants file to allow future adjustment. Ensure clearCache() is wired into the logout flow in the auth layer.
Testing Requirements
Unit tests with flutter_test: mock the local storage adapter (Hive box or SharedPreferences) using a fake in-memory implementation. Test the full state machine: (1) first fetch — no cache exists, network succeeds, cache written; (2) second fetch — cache valid, network succeeds, live result returned and cache refreshed; (3) network failure — cache valid (<24h), cached result returned; (4) network failure — cache stale (>24h), exception thrown; (5) clearCache() — subsequent read returns null. Integration test: use flutter_test with a temporary Hive directory; verify serialisation round-trip preserves all MentorLocation fields. Verify organisation-scoped cache keys isolate data between two different org IDs.
The dual BLoC state machines (map view state + filter state) may introduce subtle synchronisation bugs where filter changes do not correctly re-trigger viewport queries, causing stale data to appear on the map.
Mitigation & Contingency
Mitigation: Define all BLoC state transitions in a state diagram before implementation. Use flutter_bloc's BlocObserver in development mode to log every state transition. Write explicit unit tests for filter-change → re-query transitions.
Contingency: If state synchronisation bugs appear in integration testing, refactor to a single unified BLoC that owns both map viewport state and filter state, eliminating cross-BLoC dependencies.
Cached mentor location data may become stale (mentors move, pause, or revoke consent) and coordinators in offline mode could be shown incorrect mentor information, leading to wasted outreach.
Mitigation & Contingency
Mitigation: Display a clear timestamp on cached data indicating when it was last synced. Set cache TTL to 24 hours and show an 'offline — data from [date]' banner. Revoked consent removes the mentor from the cache on next successful sync via contact-cache-sync-repository.
Contingency: If cache staleness causes user complaints, reduce TTL to 4 hours and implement background sync on app foreground. Accept that very-recently-revoked mentors may appear briefly in offline mode — document this as a known limitation in the privacy policy.