User Story #601
Updated by Islam Mansoori 22 days ago
### π Story
> As **Rahul (a citizen)**, I want to receive a push notification on my phone when the status of my submitted report changes, so that I am informed of progress without having to open the app to check manually.
---
### π― Goal & Context
After submitting a report, citizens have no visibility into what happens next unless they actively return to the app. This creates anxiety and erodes trust in the platform. Push notifications close that feedback loop passively β the citizen is informed at the moment something meaningful changes, without any effort on their part.
This feature spans three layers: the backend must detect and trigger the notification event, the infrastructure layer must deliver it reliably across platforms, and the frontend must handle the notification tap and route the user correctly.
---
### β
In-Scope
- Push notification triggered when a report status changes to **Assigned** or **Resolved**
- Notification delivered to the citizen who submitted the report
- Tapping the notification opens the app and navigates directly to that report's detail screen
- Opt-in permission request flow on first launch (iOS and Android)
- Notification permission state respected β no silent failures surfaced to the user
- Device token registration and management
### β Out-of-Scope
- Notifications for status changes other than Assigned and Resolved (e.g., Submitted β Assigned intermediate states are not separately notified unless scoped in)
- Email or SMS notification (separate story)
- In-app notification centre / inbox (separate story)
- Bulk or broadcast notifications to all citizens
- Admin-triggered manual notifications
---
### π£ Notification Content Spec
| **Trigger** | **Title** | **Body** |
| --------------------- | -------------------------- | ------------------------------------------------------------------------------------- |
| Status β **Assigned** | "Update on your report" | "Your report '[Report Title]' has been assigned to a team and is being reviewed." |
| Status β **Resolved** | "Your report is resolved!" | "Great news β '[Report Title]' has been marked as resolved. Thank you for reporting." |
**Rules:**
- `[Report Title]` must be dynamically injected β never send a generic notification
- Title and body must not exceed platform character limits (iOS: ~50 chars title / ~100 chars body visible in lock screen)
- Long report titles must be truncated gracefully with ellipsis β do not let them break the notification layout
- Notifications must be sent in the **language preference** set on the citizen's profile (if multi-language is in scope for this sprint β confirm with PM)
---
### π Notification Behaviour by Platform
| **Scenario** | **Expected Behaviour** |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| App is in the **foreground** | Show an in-app banner/toast β do not fire an OS-level notification |
| App is in the **background** | OS delivers standard push notification to device |
| App is **closed** (killed) | OS delivers standard push notification to device |
| User has **denied** notification permission | No notification sent; no error shown to user; permission state logged silently |
| User has **not yet been asked** for permission | Permission prompt shown at appropriate moment (not at first launch cold β see Permission Flow below) |
| Device is **offline** at time of send | Notification held by the push provider and delivered when device comes online (standard TTL behaviour β confirm TTL value with Infra) |
---
### π± Permission Request Flow
- Do **not** ask for notification permission immediately on first app launch β this is a known pattern that leads to high denial rates
- Trigger the permission prompt **after** a citizen successfully submits their first report, with a contextual explanation: *"Allow notifications so we can keep you updated when your report status changes."*
- If the citizen denies, do not ask again in the same session
- If the citizen denies at OS level, surface a soft prompt on the My Reports screen: *"Enable notifications to get status updates"* with a link to device settings β shown once per 30 days maximum
- Permission state (granted / denied / not-asked) must be stored and respected at all times
---
### βοΈ System Behaviour & Business Rules
- A notification must be sent **once** per status transition β duplicate notifications for the same status change on the same report are not acceptable
- If a report changes status multiple times rapidly (e.g., Assigned β Resolved within seconds), each transition triggers its own notification independently
- Notifications must only be sent to the **report owner** β never to other citizens
- If the citizen has multiple devices, the notification should be delivered to **all registered active devices**
- A device token that has been inactive for more than 90 days should be considered stale β define cleanup policy with the Infra team
- Notification delivery is **best-effort** β a failed delivery should not block or roll back the status change on the backend
---
### πΊοΈ Deep Link & Navigation Behaviour
| **Entry Point** | **Expected Navigation** |
| ------------------------------------------------------------- | --------------------------------------------------------------------------- |
| User taps notification, app is **closed** | App opens β navigates directly to Report Detail screen for that `report_id` |
| User taps notification, app is **backgrounded** | App resumes β navigates directly to Report Detail screen |
| User taps notification, app is **in foreground** | In-app banner shown β tapping it navigates to Report Detail screen |
| User **dismisses** notification without tapping | No navigation; no side effects |
| `report_id` in notification payload is **invalid or deleted** | Navigate to My Reports Dashboard (fallback); do not crash |
---
### β Error Handling
| **Scenario** | **Handling** |
| ---------------------------------------------- | --------------------------------------------------------------------------------- |
| Device token registration fails | Retry silently on next app open; do not surface to user |
| Push provider delivery failure | Log event for monitoring; do not retry more than once; do not block status update |
| Notification payload malformed | Log and discard; alert on-call if error rate exceeds threshold |
| `report_id` missing from payload | Notification tap falls back to My Reports Dashboard |
| Citizen account deleted but token still active | Token must be invalidated on account deletion β confirm with Backend |
---
### π Acceptance Criteria
| **#** | **Criteria** | **Test Method** |
| ----- | -------------------------------------------------------------------------------------------------- | ----------------------- |
| AC-1 | Citizen receives a push notification when their report status changes to **Assigned** | Manual + staging test |
| AC-2 | Citizen receives a push notification when their report status changes to **Resolved** | Manual + staging test |
| AC-3 | Notification title and body dynamically include the correct report title | Manual |
| AC-4 | Tapping the notification navigates to the correct Report Detail screen | Manual |
| AC-5 | No notification is sent if the citizen has denied push permissions | Device permission test |
| AC-6 | No duplicate notification is sent for the same status transition on the same report | Backend test |
| AC-7 | In-app banner is shown instead of OS notification when the app is in the foreground | Manual |
| AC-8 | If the deep-linked report is not found, the app navigates to My Reports Dashboard without crashing | Manual / edge case test |
| AC-9 | Notification is delivered to all active registered devices for the same citizen account | Multi-device test |
| AC-10 | Permission prompt is not shown on cold first launch β only after first report submission | Manual UX flow test |
---
### π Dependencies & Open Questions
| **#** | **Question** | **Owner** | **Status** |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------- |
| 1 | Which push notification provider are we using β FCM (Firebase), APNs direct, or a unified service like OneSignal / Expo Notifications? | Infra | **Open** |
| 2 | Should notifications be triggered for **all** future status transitions (e.g., Submitted, Rejected) or only Assigned and Resolved for this sprint? | PM (Rahul) | **Open β needs scope decision** |
| 3 | What is the agreed TTL (Time to Live) for undelivered notifications held by the provider? | Infra | Open |
| 4 | Is multi-language notification content in scope for this sprint, or English-only first? | PM (Rahul) | **Open** |
| 5 | What is the device token stale/cleanup policy β 90 days? Who owns the cleanup job? | Backend + Infra | Open |
| 6 | Should a notification be sent if the **admin** changes the status, or only when the system does it automatically? | PM (Rahul) | **Open β policy decision** |
| 7 | Is there an analytics requirement β do we need to track notification open rate / tap-through rate? | PM (Rahul) | Open |
---
### β
Definition of Done
- [ ] All 10 ACs pass QA sign-off on both iOS and Android
- [ ] Notification content reviewed and approved by PM before deployment
- [ ] No duplicate notification delivered for any tested status transition
- [ ] Deep link navigation tested for all app states (foreground, background, closed)
- [ ] Permission denial scenario tested β confirmed no notification sent and no crash
- [ ] Stale token cleanup policy documented and agreed with Infra
- [ ] Notification delivery failures logged and observable in monitoring dashboard
- [ ] Load tested: notification dispatch does not degrade status-update API response time