# Notifications Flow

# Flow: Notifications

**Document:** LLD-005
**Version:** 1.0
**Date:** 2026-02-21
**Author:** Frontend Architect (AI Agent)
**Status:** Draft
**Scope:** Push notification delivery, in-app notification center, notification types, read/unread state, deep linking, and permission handling

---

## 1. Overview

Drop's notification system provides users with transaction alerts, security notifications, and system updates. The current implementation consists of an in-app notification center (bell icon) with read/unread state management. Push notifications via Expo Push are planned for mobile but not yet implemented.

**Current state:**
- In-app notification center: Implemented (web)
- Push notifications: Not yet implemented (planned via Expo Push for mobile)
- Deep linking from notifications: Not yet configured
- Notification preferences: Implemented (push/email toggles in settings)

---

## 2. Push Notification Delivery (Planned Architecture)

### 2.1 Sequence Diagram — Push Notification Flow

```mermaid
sequenceDiagram
    actor User
    participant Mobile as Expo App
    participant API as Drop API
    participant DB as Database
    participant Push as Expo Push<br/>Service
    participant APNS as APNs / FCM

    Note over Mobile,APNS: Setup Phase (on login)
    Mobile->>Mobile: Request notification permission
    Mobile->>Push: Register for push token
    Push-->>Mobile: { expoPushToken }
    Mobile->>API: POST /api/push-token { token, platform }
    API->>DB: INSERT push_tokens (user_id, token, platform)

    Note over API,APNS: Trigger Phase (on event)
    API->>API: Transaction completed / security event
    API->>DB: INSERT notification (user_id, type, title, body)
    API->>DB: SELECT push_tokens WHERE user_id = ?
    API->>Push: POST /send { to: expoPushToken, title, body, data }
    Push->>APNS: Forward to APNs (iOS) / FCM (Android)
    APNS-->>Mobile: Push notification delivered

    Note over Mobile: Receive Phase
    Mobile->>Mobile: Display system notification
    User->>Mobile: Tap notification
    Mobile->>Mobile: Deep link to relevant screen
    Mobile->>API: PATCH /api/notifications { notificationIds: [id] }
    API->>DB: UPDATE notifications SET read = 1
```

### 2.2 In-App Notification Center (Current Implementation)

```mermaid
sequenceDiagram
    actor User
    participant App as Drop App<br/>(/notifications)
    participant API as Drop API
    participant DB as Database

    User->>App: Tap bell icon (dashboard) or navigate to /notifications
    App->>App: useAuth() — verify authenticated

    App->>API: GET /api/notifications
    API->>DB: SELECT notifications WHERE user_id = ?<br/>ORDER BY created_at DESC
    API-->>App: { data: [ { id, type, title, body, read, createdAt }, ... ] }

    App->>App: Group by date (I DAG, I GAR, older)
    App->>App: Render notification cards with icons

    Note over App: Auto-mark as read on page load
    App->>App: Collect unread notification IDs
    App->>API: PATCH /api/notifications<br/>{ notificationIds: [unread IDs] }
    API->>DB: UPDATE notifications SET read = 1<br/>WHERE id IN (?) AND user_id = ?
    API-->>App: 200 OK (fire-and-forget)
```

---

## 3. Notification Center Components

### 3.1 Component Diagram

```mermaid
graph TD
    subgraph "Notification Center Page"
        Header["Header<br/>Back button + 'Varsler' title"]
        NotificationList["NotificationList"]
        EmptyState["EmptyState<br/>Bell icon + 'Ingen varsler enna'"]
    end

    subgraph "Notification List"
        DateGroup["DateGroupHeader<br/>('I DAG', 'I GAR', date)"]
        NotificationCard["NotificationCard"]
    end

    subgraph "Notification Card"
        TypeIcon["TypeIcon<br/>(colored circle + icon)"]
        Content["Content<br/>(title, body, timestamp)"]
        UnreadDot["UnreadDot<br/>(blue indicator)"]
    end

    subgraph "Dashboard Integration"
        BellIcon["Bell Icon<br/>(header, with badge count)"]
    end

    NotificationList --> DateGroup
    DateGroup --> NotificationCard
    NotificationCard --> TypeIcon
    NotificationCard --> Content
    NotificationCard --> UnreadDot

    BellIcon -->|navigate| Header
```

---

## 4. Notification Type Table

| Type | Icon | Icon Color | Background | Title Example | Body Example | Priority |
|------|------|-----------|------------|---------------|-------------|----------|
| `transaction_complete` | ArrowUpRight | `#0B6E35` (green) | `#F0FDF4` | "Overforing til Mama Jasmina fullfort" | "2 000 kr sendt til Serbia" | Normal |
| `qr_payment` | ScanLine | `#D4A017` (gold) | `#FEF3C7` | "QR-betaling hos Ahmetov Kebab" | "129 kr betalt" | Normal |
| `security` | Smartphone | `#3B82F6` (blue) | `#EFF6FF` | "Ny palogging fra iPhone" | "Oslo, Norge" | High |
| `rate_update` | TrendingUp | `#D4A017` (gold) | `#FEF3C7` | "Valutakurs oppdatert" | "1 NOK = 11.70 RSD" | Low |
| `system` | Bell | `#6B7280` (gray) | `#F3F4F6` | "Systemoppdatering" | "Drop er oppdatert til v0.2.0" | Low |
| `promotional` | — | `#6B7280` (gray) | `#F3F4F6` | "Nytt tilbud" | "0% gebyr denne uken!" | Low |

### 4.1 Priority Levels

| Priority | Behavior | Push | In-App |
|----------|----------|------|--------|
| High | Immediate delivery, sound, badge | Yes (when implemented) | Top of list, bold styling |
| Normal | Standard delivery | Yes (when implemented) | Normal styling |
| Low | Batched delivery | Optional (user preference) | Normal styling |

---

## 5. Deep Link Routing Table (Planned)

| Notification Type | Deep Link Target | Web Route | Mobile Route |
|-------------------|-----------------|-----------|--------------|
| `transaction_complete` | Transaction detail | `/transactions?id={txId}` | `/(tabs)/history?id={txId}` |
| `qr_payment` | Transaction detail | `/transactions?id={txId}` | `/(tabs)/history?id={txId}` |
| `security` | Security settings | `/profile/security` | `/(tabs)/profile` |
| `rate_update` | Send money (with rate) | `/send` | `/(tabs)/send` |
| `system` | Notification center | `/notifications` | `/notifications` |
| `promotional` | Landing or feature page | `/` or feature URL | App home |

### 5.1 Deep Link Format

| Platform | Format | Example |
|----------|--------|---------|
| Web | URL path | `https://getdrop.no/transactions?id=tx_rem_1` |
| Mobile (planned) | Custom scheme | `drop://transactions/tx_rem_1` |
| Expo push data | JSON payload | `{ "type": "transaction_complete", "targetId": "tx_rem_1" }` |

---

## 6. Permission Handling

### 6.1 Notification Permission Flow

| Platform | Permission Request | First Time | Denied | Settings Redirect |
|----------|-------------------|------------|--------|-------------------|
| iOS (Expo) | `Notifications.requestPermissionsAsync()` | System dialog: "Drop would like to send you notifications" | Returns `{ status: 'denied' }` | `Linking.openSettings()` |
| Android (Expo) | `Notifications.requestPermissionsAsync()` | System dialog (Android 13+): "Allow Drop to send you notifications?" | Returns `{ status: 'denied' }` | `Linking.openSettings()` |
| Web | `Notification.requestPermission()` | Browser prompt: "getdrop.no wants to show notifications" | Blocked; user must reset in browser settings | Site settings via address bar |

### 6.2 Permission State Machine

```mermaid
stateDiagram-v2
    [*] --> NotDetermined: First launch

    NotDetermined --> Requesting: App requests permission
    Requesting --> Granted: User allows
    Requesting --> Denied: User denies

    Granted --> Active: Push token registered
    Active --> Disabled: User toggles off in Drop settings
    Disabled --> Active: User toggles on in Drop settings

    Denied --> SettingsRedirect: App shows "enable in settings" prompt
    SettingsRedirect --> Granted: User enables in OS settings
    SettingsRedirect --> Denied: User keeps disabled
```

---

## 7. Notification Preferences (Settings Integration)

Users control notification delivery via `/profile/notifications`:

| Setting | API Field | Effect |
|---------|-----------|--------|
| Push notifications ON | `pushEnabled: true` | Push tokens active, notifications delivered |
| Push notifications OFF | `pushEnabled: false` | Push tokens retained but not used for delivery |
| Email notifications ON | `emailEnabled: true` | Email alerts sent for high-priority events |
| Email notifications OFF | `emailEnabled: false` | No email alerts |

**API:** `PATCH /api/settings { pushEnabled: boolean, emailEnabled: boolean }`

---

## 8. Time Formatting

| Condition | Format | Example |
|-----------|--------|---------|
| Today | "I dag kl. HH:MM" | "I dag kl. 14:32" |
| Yesterday | "I gar kl. HH:MM" | "I gar kl. 18:45" |
| Older | "DD.MM.YYYY kl. HH:MM" | "19.02.2026 kl. 09:00" |

---

## 9. Platform Differences

| Feature | Web | Mobile |
|---------|-----|--------|
| Notification center | `/notifications` page with BottomNav | Not implemented |
| Bell icon badge | Dashboard header (unread count) | Not implemented |
| Push notifications | Not applicable (web push planned) | Not implemented (Expo Push planned) |
| Auto-read on view | Yes (marks all unread as read on page load) | N/A |
| Deep linking | URL-based routing | Not configured |
| Notification grouping | Date-based (I DAG, I GAR) | N/A |
| Permission handling | Browser Notification API | Expo Notifications API |

---

## 10. Data Schema

### 10.1 Notifications Table

| Column | Type | Description |
|--------|------|-------------|
| `id` | TEXT PK | Format: `noti_` + 16 hex chars |
| `user_id` | TEXT FK | References `users(id)` |
| `type` | TEXT | `transaction_complete`, `qr_payment`, `security`, `rate_update` |
| `title` | TEXT | Notification title (Norwegian) |
| `body` | TEXT | Notification body text |
| `read` | INTEGER | 0 = unread, 1 = read |
| `created_at` | TEXT | ISO timestamp |

### 10.2 API Endpoints

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/notifications` | List all notifications for user |
| PATCH | `/api/notifications` | Mark notifications as read (max 100 IDs) |

---

## 11. Accessibility Considerations (WCAG 2.1 AA)

| Requirement | Implementation |
|-------------|---------------|
| Badge count | Bell icon badge uses aria-label "X uleste varsler" |
| Read/unread | Unread dot uses both visual indicator (blue dot) and aria attributes |
| Notification list | Semantic list markup with role="list" |
| Empty state | Descriptive text "Ingen varsler enna" with Bell icon |
| Time formatting | Relative time ("I dag kl. 14:32") for recent, absolute for older |
| Auto-read | Fire-and-forget PATCH does not interrupt user reading |
| Push permission | Clear explanation before requesting system permission |

---

## 12. Cross-References

- **Notifications API:** `GET/PATCH /api/notifications` — See [API Reference](../../backend/API-REFERENCE.md)
- **Settings API:** `PATCH /api/settings` (push/email toggles) — See [API Reference](../../backend/API-REFERENCE.md)
- **Notifications schema:** `notifications` table — See [Database Schema](../../backend/DATABASE-SCHEMA.md)
- **Settings schema:** `settings` table — See [Database Schema](../../backend/DATABASE-SCHEMA.md)
- **Component overview:** See [component-overview.md](../hld/component-overview.md)
- **Figma notifications screen:** `mockups/figma-make-export/src/app/screens/Notifications.tsx`
- **Web notifications page:** `src/drop-app/src/app/notifications/page.tsx` — See [PAGES.md](../../frontend/PAGES.md)
- **Profile settings flow:** See [flow-profile-settings.md](flow-profile-settings.md)