Implement NotificationPreferencesRepository with upsert and opted-in defaults
epic-scenario-push-engagement-foundation-task-003 — Implement the Dart NotificationPreferencesRepository class using the Supabase client. Provide methods: getPreferences(userId), upsertPreference(userId, scenarioType, optedIn), and getAllForUser(userId). Upsert must default opted_in to true when no row exists. Use Riverpod for dependency injection.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Define a NotificationPreference Dart model with fromJson/toJson using the standard Supabase response map format. For the 'virtual default' pattern in getPreferences() — when the Supabase query returns an empty list, return NotificationPreference(userId: userId, scenarioType: scenarioType, optedIn: true, ...) without inserting a row. This avoids unnecessary writes and keeps the database clean until the user explicitly changes a preference. For getAllForUser(), define a list of known ScenarioType enum values and merge the database results against that list, filling in opted_in=true defaults for missing types.
Register the repository with a Provider
Testing Requirements
Unit tests using flutter_test with a mocked SupabaseClient (via mockito or manual stub). Test cases: (1) getPreferences returns NotificationPreference with opted_in=true when Supabase returns empty list. (2) getPreferences returns correct model when Supabase returns a row with opted_in=false. (3) upsertPreference calls upsert with correct payload and returns mapped model.
(4) upsertPreference wraps SupabaseException in NotificationPreferencesException. (5) getAllForUser returns list with opted_in=true defaults for missing scenario types. Minimum 5 unit tests. No integration tests in this task — those are covered by the scheduler integration tests.
FCM service account key and APNs certificate configuration may be missing or misconfigured in the Supabase Edge Function secrets store, blocking end-to-end push delivery testing until resolved by the infrastructure owner.
Mitigation & Contingency
Mitigation: Raise a credentials-setup task in the project board at epic start; document the exact secret names required in scenario-evaluation-config so the infrastructure owner can provision them independently of development work.
Contingency: Implement a mock push-notification-dispatcher stub that records payloads to the database for local testing, allowing the rest of the feature to proceed while credentials are obtained.
Incorrect RLS policies on the scenario_notifications or notification_preferences tables could allow one user to read or modify another user's notification records, constituting a data privacy breach.
Mitigation & Contingency
Mitigation: Write dedicated RLS policy tests using Supabase's built-in test framework before any application code touches the tables; require a peer security review of all policy definitions before merging.
Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's read policy, notify the security lead, and deploy a hotfix with corrected policies before re-enabling access.
Norwegian Bokmål ARB localisation strings for all scenario message templates may not be available at implementation time, causing content-builder tests to fail and delaying integration.
Mitigation & Contingency
Mitigation: Define all required ARB message keys in a tracked document shared with the content owner at epic kickoff; use English placeholder strings that follow the final format so template injection logic can be tested independently.
Contingency: Ship with English-only strings in the first release and gate Norwegian strings behind a feature flag that is enabled once translations are reviewed and approved.