Implement SupabaseRealtimeSubscriptionService for notifications
epic-in-app-notification-centre-foundation-task-006 — Create a SupabaseRealtimeSubscriptionService that opens a Supabase Realtime channel scoped to the current authenticated user's notifications (filter: user_id=eq.{userId}). Listen for INSERT events and emit new Notification objects to a Stream<Notification>. Handle channel reconnection on auth state changes, implement dispose() to unsubscribe cleanly, and expose a connectionStatus stream for error/offline states. Use Riverpod or bloc-friendly stream exposure.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Use `supabase.channel('notifications-{uuid}').on(RealtimeListenTypes.postgresChanges, ChannelFilter(event: 'INSERT', schema: 'public', table: 'notifications', filter: 'user_id=eq.$userId'), callback)` API. Use a `StreamController
})` callback and map `RealtimeSubscribeStatus` values to your `RealtimeConnectionStatus` enum. Store the auth subscription as a `StreamSubscription` and cancel it in `dispose()`. Expose via Riverpod as a `StateNotifierProvider` or a plain `Provider` that is `.autoDispose`-aware so the channel is closed when no UI is listening. For the reconnection path, call `channel.unsubscribe()` then re-call `subscribe(newUserId)` — do not reuse the old channel object.
Testing Requirements
Two test layers: (1) Unit tests with mocked `SupabaseClient` and `RealtimeChannel` — verify `subscribe()` constructs the correct filter string, `dispose()` calls `unsubscribe()` on the channel, malformed payloads do not propagate errors to the stream, and auth state change triggers re-subscription. (2) Integration test against a local Supabase instance — insert a notification row for the subscribed userId and assert the stream emits a correctly typed `Notification` within 3 seconds; insert a row for a different userId and assert no emission. Use `StreamQueue` from the `async` package for stream testing. Target 85%+ branch coverage on the service class.
Supabase Realtime channels on mobile networks can drop silently. If reconnection logic is flawed, users miss notifications without knowing it, undermining the audit-trail guarantee.
Mitigation & Contingency
Mitigation: Implement exponential-backoff reconnection with a maximum of 5 retries; expose a channel-status stream to the BLoC so it can trigger a full-fetch fallback when the channel reconnects after a gap.
Contingency: If Realtime reliability proves insufficient in production, fall back to polling the repository every 60 seconds as a background supplement to the Realtime channel.
Coordinator and org-admin RLS expansions require joining user_roles and org_memberships tables. An incorrect policy could expose notifications to wrong users or block legitimate access entirely.
Mitigation & Contingency
Mitigation: Write dedicated RLS integration tests for each role (peer mentor, coordinator, org admin) using separate Supabase test projects. Review policies with the security checklist before merging.
Contingency: If an RLS defect is discovered post-deployment, disable the expanded-scope policy and revert to user-scoped-only access while a corrected migration is prepared and tested.
JSONB payload structure may vary across notification types created by different Edge Functions (reminder, expiry, scenario, pause). Missing or renamed fields will cause runtime parse failures.
Mitigation & Contingency
Mitigation: Define a canonical NotificationPayload union type in a shared schema document. Each Edge Function must validate its payload against this schema before inserting. Add fallback parsing with default values in the domain model.
Contingency: Wrap all payload parsing in try/catch and log malformed payloads to a monitoring channel; render a generic notification item rather than crashing when the payload cannot be parsed.