# 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?