Wire PushNotificationDispatcher for FCM/APNs
epic-scenario-based-follow-up-prompts-foundation-task-008 — Implement the PushNotificationDispatcher infrastructure class that sends push notifications via both FCM (Android) and APNs (iOS). Integrate with the existing FCM token manager to resolve device tokens by peer_mentor_id. Expose a dispatch(NotificationPayload payload) method accepting title, body, data map, and target mentor ID. Handle token refresh, invalid token removal, and delivery failure logging. Use the Supabase edge function as the secure server-side sender to avoid embedding FCM credentials in the client.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Implement PushNotificationDispatcher as a plain Dart class (not a widget) injected via Riverpod. The class should hold a reference to SupabaseClient and invoke a named Edge Function (e.g., 'dispatch-push-notification') via supabase.functions.invoke(). The Edge Function receives { mentorId, title, body, data } and resolves the FCM token server-side before calling the FCM HTTP v1 API. On the Flutter side, keep the dispatcher thin — its only responsibilities are payload assembly and result parsing.
Use a sealed class (DispatchResult) with variants sent, noToken, tokenRevoked, failed for exhaustive handling by callers. Register device tokens using the platform field from the device_token schema to route APNS-specific headers correctly in the Edge Function. Never log the raw FCM token string at info level — use a hashed or truncated form for debugging.
Testing Requirements
Unit tests (flutter_test) with a mocked Supabase client and a stubbed Edge Function HTTP client. Cover: (1) successful dispatch returns DispatchResult.sent, (2) invalid FCM token triggers token deletion and returns DispatchResult.tokenRevoked, (3) no device token for mentor returns DispatchResult.noToken without exception, (4) Edge Function HTTP 500 returns DispatchResult.failed with logged error, (5) token refresh upserts new token and deletes old. Integration test against a local Supabase emulator verifying device_token table mutations. Minimum 80% line coverage on PushNotificationDispatcher.
Supabase RLS policies for chapter-scoped rule access may interact unexpectedly with service-role keys used by the Edge Function, potentially blocking backend reads or leaking cross-chapter data.
Mitigation & Contingency
Mitigation: Write and review RLS policies in isolation with automated policy tests before merging; define a dedicated service-role bypass policy scoped to the edge function's Postgres role.
Contingency: If RLS blocks the edge function, temporarily use a bypass policy with audit logging while a permanent fix is implemented; escalate to a Supabase security review.
FCM device tokens become invalid when users reinstall the app or revoke permissions; stale tokens cause silent delivery failures that are hard to detect without explicit error handling.
Mitigation & Contingency
Mitigation: Implement token invalidation handling in PushNotificationDispatcher that removes stale tokens from the database on FCM 404/410 responses; log all delivery failures with structured output.
Contingency: If token hygiene proves unreliable, add a periodic token refresh job that re-registers all active users' tokens via the FCM registration endpoint.