Wire permission manager to FCM token registration
epic-push-notification-delivery-foundation-task-013 — Integrate NotificationPermissionManager with FCMTokenManager so that granting permission automatically triggers FCM token registration without requiring a new app launch. In FCMTokenManager, expose a registerIfPermissionGranted() method that NotificationPermissionManager calls upon receiving a granted result. Ensure proper Riverpod dependency injection so both services share the same auth session context.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Model the integration as a unidirectional callback: NotificationPermissionManager.onPermissionGranted is a VoidCallback injected at construction via Riverpod. FCMTokenManager exposes a method registerIfPermissionGranted() that (1) calls FirebaseMessaging.instance.getToken(), (2) upserts the token into Supabase device_tokens with platform + user_id, (3) sets up FirebaseMessaging.instance.onTokenRefresh listener to handle automatic rotation. Provide both managers as Riverpod Provider
On Android API 33+ check Build.VERSION.SDK_INT before calling requestPermission to avoid redundant dialogs.
Testing Requirements
Unit tests (flutter_test + Mockito): (1) permission granted callback invokes registerIfPermissionGranted(); (2) permission denied — registerIfPermissionGranted() is not called; (3) FCM token retrieval failure — error handled gracefully without crash; (4) Riverpod provider graph resolves without circular dependency. Widget test: simulate OS permission grant on mock PermissionManager and assert FCMTokenManager.register() was called exactly once. Integration test (real device/TestFlight): grant permission, verify token row appears in Supabase device_tokens table. Target 80% coverage on both managers.
iOS only allows one system permission prompt per app install. If the rationale dialog timing or content is wrong the user may permanently deny permissions during onboarding, permanently blocking push delivery for that device with no recovery path short of manual system settings navigation.
Mitigation & Contingency
Mitigation: Design and user-test the rationale dialog content and trigger point (after onboarding value-demonstration step, not at first launch). Implement the settings-deep-link fallback in NotificationPermissionManager so the permission state screen always offers a path to system settings if denied.
Contingency: If denial rates are high in TestFlight testing, revise the rationale copy and trigger timing before production release. Ensure the in-app notification centre provides full value without push so denied users are not blocked from the feature.
FCM token rotation callbacks can fire at any time, including during app termination or network outage. If the token rotation is not persisted reliably the backend trigger service will dispatch to a stale token, resulting in silent notification failures that are hard to diagnose.
Mitigation & Contingency
Mitigation: Persist token rotation updates with a local queue that retries on next app foreground if network is unavailable. Use Supabase upsert by (user_id, device_id) to prevent duplicate token rows and ensure the latest token always wins.
Contingency: If token staleness is observed in production, add a token validity check on each app foreground and force a re-registration if the stored token does not match the FCM-reported current token.
Incorrect RLS policies on notification_preferences or fcm_tokens could expose one user's preferences or device tokens to another user, or could block the backend Edge Function service role from reading token lists needed for dispatch, silently dropping all notifications.
Mitigation & Contingency
Mitigation: Write explicit RLS policy tests using the Supabase test harness covering user-scoped read/write, service-role read for dispatch, and cross-user access denial. Review policies during code review with a security checklist.
Contingency: Maintain a rollback migration that reverts the RLS changes, and add an integration test in CI that asserts the service role can query all tokens and that a normal user JWT cannot access another user's token rows.