Polish navigation, Quran flows, and sharing UX
This commit is contained in:
248
docs/notification-plan.md
Normal file
248
docs/notification-plan.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# Notification Plan (Alerts + Inbox)
|
||||
|
||||
Last updated: 2026-03-16
|
||||
Owner: Product + Mobile
|
||||
Scope: `jamshalat_diary` (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?
|
||||
Reference in New Issue
Block a user