249 lines
7.5 KiB
Markdown
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?
|