Implement PushNotificationService wrapper
epic-assignment-follow-up-reminders-foundation-task-007 — Implement the PushNotificationService infrastructure wrapper that encapsulates FCM token management and push dispatch. Expose sendToUser(userId, title, body, data) and sendToTopic(topic, payload) methods. Integrate with the existing FCM token manager. Handle platform-specific notification channels for Android and iOS. Provide a mock implementation for testing environments.
Acceptance Criteria
Technical Requirements
Implementation Notes
Define an abstract PushNotificationService interface first (abstract class in Dart), then provide two concrete implementations: FirebasePushNotificationService and MockPushNotificationService. This inversion of control is what makes task-011 testable. For FCM token resolution, call the existing FCM token manager (do not duplicate token storage logic). For Android channels, call flutter_local_notifications' AndroidFlutterLocalNotificationsPlugin.createNotificationChannel() during app startup — do this in main.dart or an AppInitializer, not lazily on first send.
For iOS, call FirebaseMessaging.instance.requestPermission() with alert: true, badge: true, sound: true. Wrap all FirebaseMessaging calls in try/catch and convert platform exceptions to domain exceptions before rethrowing. The sendToTopic method should only be used for broadcast scenarios (e.g., org-wide announcements); prefer sendToUser for targeted reminder notifications to maintain auditability.
Testing Requirements
Unit tests (task-011) cover the service in isolation using MockPushNotificationService. For this implementation task, verify manually on a physical Android device (channel creation) and iOS simulator (permission prompt) during development. Ensure the mock implements the identical abstract interface so no test can accidentally import the real service. Confirm the Riverpod provider switch between real/mock is driven by a compile-time or environment flag, not runtime branching that could ship the mock to production.
Adding last_contact_date to the assignments table may conflict with existing RLS policies or trigger-based logic that monitors the assignments table. If the migration is not carefully reviewed, existing assignment management features could break in production.
Mitigation & Contingency
Mitigation: Review all existing triggers, policies, and foreign key constraints on the assignments table before writing the migration. Run the migration against a staging Supabase instance with production-like data and execute the full existing test suite before merging.
Contingency: Roll back the migration using Supabase's versioned migration history. Apply the schema change as an additive-only migration (nullable column with default) to ensure zero downtime and reversibility.
The PushNotificationService wraps an existing FCM integration whose internal API contract may have changed or may not expose the payload formatting required for deep-link CTAs. Misalignment discovered late delays the dispatch service epic.
Mitigation & Contingency
Mitigation: Before implementing the wrapper, read the existing push notification integration code and confirm the method signatures, payload structure, and token management model. Agree on a stable interface contract in a shared Dart abstract class.
Contingency: If the existing service is incompatible, implement a thin adapter layer that translates reminder payloads to the existing service's format, isolating the reminder feature from upstream changes.
Incorrect RLS policies on notification_log could allow coordinators to read reminder records belonging to peer mentors in other chapters, exposing sensitive assignment information across organisational boundaries.
Mitigation & Contingency
Mitigation: Write explicit RLS policies with integration tests that assert cross-chapter queries return zero rows. Use Supabase's built-in auth.uid() and join through the org membership tables to scope all queries.
Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's SELECT policy, deploy a corrected policy, and audit recent queries in Supabase logs for any cross-boundary reads.