Files
jamshalat-diary/docs/notification-plan.md
2026-03-18 00:45:24 +07:00

249 lines
7.5 KiB
Markdown

# Notification Plan (Alerts + Inbox)
Last updated: 2026-03-16
Owner: Product + Mobile
Scope: `JamShalat` (Flutter)
## Implementation Status (2026-03-17)
- Phase 1: Implemented.
- Phase 2: Implemented (Notification Center + unread badge + inbox persistence).
- Phase 3: Implemented (daily checklist reminder scheduling, streak-risk messages, weekly summary, quiet-hours and daily cap preferences).
- Phase 4: Partially implemented via remote-content sync and remote-push ingestion bridge.
- External pending for full Phase 4: SDK/provider wiring (FCM/APNs credentials, token registration, backend push dispatch).
## 1. Problem Statement
Current app has mixed meaning for "notification":
- Device push alert (time-sensitive, appears in system drawer)
- In-app message feed (read-later information)
This causes unclear UX around the bell icon and settings.
## 2. Product Model
Split into two clear products:
1. **Alerts** (`Pemberitahuan`)
- Time-sensitive pushes to OS notification drawer.
- Can ring/vibrate.
- Expire quickly.
2. **Inbox** (`Pesan`)
- In-app list of messages user can read later.
- Has read/unread state.
- No mandatory sound.
Default rule:
- **Push only**: urgent + expiring event.
- **Inbox only**: informational/non-urgent event.
- **Push + Inbox**: important event that also needs follow-up context.
## 3. Terminology (User-Facing)
- Bell icon label/context: `Pemberitahuan`
- Notification center tabs:
- `Alarm` (alert history + system-triggered items)
- `Pesan` (inbox/read-later)
## 4. Event Policy Matrix
| Event | Push Drawer | Inbox | Sound | Expiry |
|---|---|---|---|---|
| Adzan time entered | Yes | Optional (off by default) | Yes | At end of prayer window |
| Iqamah reminder | Yes | No | Yes | At iqamah time |
| Next prayer in 10 minutes | Optional | No | Soft | At prayer start |
| Daily checklist reminder | Yes (if enabled) | No | Optional | End of day |
| Streak at risk (Tilawah/Dzikir) | Optional | Yes | No | End of day |
| Weekly worship summary | No | Yes | No | 7 days |
| Permission blocked (notif/exact alarm/location) | Yes | Yes | No | When resolved |
| Schedule/location stale | Yes | Yes | No | When resolved |
| New app content (Doa/Hadits) | No | Yes | No | 30 days |
## 5. Architecture
## 5.1 Channels
- **Local scheduled alerts** (already started in app): Adzan + Iqamah.
- **In-app inbox storage** (new): persisted read/unread messages.
- **Remote push** (future): FCM/APNs for server-driven campaigns/events.
## 5.2 Data Sources
- Prayer schedules: `MyQuranSholatService`.
- User prefs: `AppSettings` + new notification preference model.
- Device status: notification permission, exact alarm permission, location service.
## 5.3 Decision Engine
Input:
- event type
- urgency
- TTL
- user preferences
- cooldown/frequency cap
Output:
- deliver to `push`, `inbox`, or both.
Pseudo rule:
```text
if event.isTimeCritical && event.ttlShort:
push
if event.needsFollowUp || event.referenceContent:
inbox
if event.push && event.inboxPolicy == mirror:
push + inbox
```
## 6. Data Model
## 6.1 Inbox Item (Hive)
Suggested new box: `notification_inbox`
```json
{
"id": "uuid",
"type": "streak_risk | summary | system | content | prayer",
"title": "string",
"body": "string",
"createdAt": "iso8601",
"expiresAt": "iso8601|null",
"readAt": "iso8601|null",
"isPinned": false,
"deeplink": "/route/path",
"meta": {
"cityId": "string|null",
"prayerKey": "fajr|dhuhr|asr|maghrib|isha|null",
"date": "yyyy-MM-dd|null"
},
"source": "local|remote",
"dedupeKey": "string"
}
```
## 6.2 Preferences
Use existing `AppSettings` for:
- `adhanEnabled` (per prayer)
- `iqamahOffset` (per prayer)
Add new fields (either in `AppSettings` or separate box):
- `alertsEnabled` (global)
- `inboxEnabled` (global)
- `streakRiskEnabled`
- `dailyChecklistReminderEnabled`
- `weeklySummaryEnabled`
- `quietHoursStart` / `quietHoursEnd`
- `maxNonPrayerPushPerDay` (default 2)
## 6.3 Badge Source
- Bell badge count = unread inbox count only.
- Do not include "already fired system push" count.
## 7. Local Notification Spec (Current + Next)
## 7.1 Channels
- `adhan_channel`: max importance, sound on.
- `iqamah_channel`: high importance, sound on.
- Later:
- `habit_channel` (streak/checklist)
- `system_channel` (permission/location)
## 7.2 Permission Strategy
- Request `POST_NOTIFICATIONS` and exact alarm capability only when needed.
- If denied:
- create Inbox warning item
- show non-blocking UI notice
## 7.3 Scheduling Rules
- Keep 2-day rolling window (today + tomorrow).
- Resync on:
- app start
- city change
- prayer settings change
- adzan toggle change
- daily boundary change (00:05 local)
- Deduplicate by key: `cityId + date + prayer + kind(adhan/iqamah)`.
## 8. UX Specification
## 8.1 Bell Icon
- Tap action: open Notification Center.
- Badge: unread inbox count.
- Long-press (optional): quick actions
- toggle `Alarm Sholat`
- open Iqamah settings
- sync now
## 8.2 Notification Center Screen
Tabs:
- `Alarm`
- recent fired alerts (read-only timeline, optional v2)
- `Pesan`
- unread/read list with filters (`Semua`, `Belum Dibaca`, `Sistem`)
Item actions:
- Tap: open deeplink target.
- Swipe: mark read/unread.
- Optional: pin important item.
## 8.3 Copy Guidelines
- Urgent: concise, action-first.
- Inbox: context-rich but short (max ~120 chars body preview).
- Prayer push examples:
- `Adzan • Subuh`
- `Waktu sholat Subuh telah masuk.`
## 9. Deep Link Map
- Permission blocked -> `/settings` (notifications section)
- Location disabled -> `/settings` or location setup section
- Streak risk Tilawah -> `/quran`
- Streak risk Dzikir -> `/tools/dzikir`
- Weekly summary -> `/laporan`
- Prayer event details -> `/` (Beranda)
## 10. Analytics
Track:
- `notif_push_scheduled`
- `notif_push_fired`
- `notif_push_opened`
- `notif_inbox_created`
- `notif_inbox_opened`
- `notif_mark_read`
- `notif_settings_changed`
- `notif_permission_denied`
Dimensions:
- `event_type`
- `channel` (`push|inbox|both`)
- `city_id`
- `simple_mode`
## 11. Rollout Plan
## Phase 1 (Now)
- Stabilize prayer alerts (Adzan + Iqamah) local scheduling.
- Working sound toggle in hero card and settings.
- Permission and exact-alarm checks.
## Phase 2
- Build Notification Center page + unread badge.
- Add inbox persistence and read/unread actions.
- Add system warning messages to inbox.
## Phase 3
- Add non-prayer reminders (checklist, streak risk, weekly summary).
- Add cooldown/frequency cap and quiet hours.
## Phase 4
- Remote push integration (FCM/APNs).
- Server-defined campaigns/content updates.
## 12. Acceptance Criteria
- Toggling adzan off removes all pending adzan/iqamah notifications.
- Toggling adzan on schedules valid upcoming notifications for enabled prayers.
- Changing iqamah minutes updates future scheduled iqamah alerts immediately.
- Bell opens Notification Center (after Phase 2).
- Unread badge count reflects inbox unread only.
- No duplicate notifications for same prayer/time/city.
## 13. QA Checklist
- Android 13/14: permission denied -> graceful fallback.
- Exact alarm disabled -> user warning path works.
- Timezone changes -> schedule recalculates correctly.
- Day rollover -> next-day notifications still present.
- City changes -> old city schedules removed; new city schedules added.
- Device reboot (future): optional reschedule receiver if needed.
## 14. Open Decisions
1. Should adzan events be mirrored into inbox by default?
2. Should quiet hours suppress non-prayer push only, or all push except adzan?
3. Should `Alarm` tab show only fired events or also pending schedule preview?