# NexGate Messaging — Product Requirements & Feature Flows

**NexGate / QBIT SPARK | Version 1.0**
*What the messaging platform does — rules, flows, permissions, scenarios*

---

## Table of Contents

1. [Document Purpose](#1-document-purpose)
2. [User Identity & Discovery](#2-user-identity--discovery)
3. [Contact Sync](#3-contact-sync)
4. [Inbox Model](#4-inbox-model)
5. [Messaging Permissions](#5-messaging-permissions)
6. [Call Permissions](#6-call-permissions)
7. [1:1 Messaging Flows](#7-11-messaging-flows)
8. [Group Chat Flows](#8-group-chat-flows)
9. [Commerce DM Flows](#9-commerce-dm-flows)
10. [Offer Session Flows](#10-offer-session-flows)
11. [Shareable Content](#11-shareable-content)
12. [Message Interactions](#12-message-interactions)
13. [Voice & Video Call Flows](#13-voice--video-call-flows)
14. [Group Call Flows](#14-group-call-flows)
15. [Offline & Notification Flows](#15-offline--notification-flows)
16. [Privacy & Safety](#16-privacy--safety)
17. [Shop Inbox & Staff Access](#17-shop-inbox--staff-access)
18. [Notification Settings](#18-notification-settings)
19. [Edge Cases & Scenarios](#19-edge-cases--scenarios)

---

## 1. Document Purpose

This document defines WHAT NexGate messaging does — the rules,
user flows, permissions, and scenarios for every messaging feature.

It is the product reference document. It does not describe HOW
features are built technically. For technical architecture see:
- Doc 3: Phase 2 Production Architecture
- Doc 5: Private Chat & Calls Deep Dive

**Audience:** Product, mobile developers, QA, design.

---

## 2. User Identity & Discovery

### Identity on NexGate

```
Every NexGate user has:
  @username      unique, chosen at registration
                 shown publicly on VP Feed
                 used for messaging: @kibuti

  Display name   full name shown in conversations
                 can differ from username

  Phone number   used for registration + auth
                 NOT shown publicly by default
                 discoverable only with user permission

  NexGate ID     internal UUID
                 never shown to users
```

### How Users Find Each Other

```
Discovery mechanism          Requires contact sync?
──────────────────────────────────────────────────────
VP Feed follow               ❌ No
VP Shop (buyer/seller)       ❌ No
Username search (@kibuti)    ❌ No
QR code scan                 ❌ No
Shareable profile link       ❌ No
Group chat membership        ❌ No
Phone number search          ❌ No (type manually)
Contact sync (phonebook)     ✅ Yes (optional)
```

NexGate has multiple discovery paths.
Contact sync is one option — not the only one.

### Discoverability Settings

```
Two independent settings:

  "Sync my contacts"
    Find NexGate users in my phonebook
    Default: OFF (user must opt in)

  "Let others find me via phone number"
    If someone has my number → can find me
    Default: ON (can turn off)

These are INDEPENDENT:
  I can sync contacts without being findable
  I can be findable without syncing contacts
```

---

## 3. Contact Sync

### Rules

```
  ✅ Optional — never mandatory
  ✅ Can be enabled/disabled anytime
  ✅ Can delete synced data anytime from settings
  ✅ Only finds users who have discoverability ON
  ❌ Not required to use messaging
  ❌ Not required for commerce DMs
  ❌ Contacts data never shared with third parties
  ❌ Non-NexGate contacts never stored on server
```

### Privacy-Preserving Implementation

```
How it works when user accepts:

  Step 1: Phone numbers hashed ON DEVICE
    sha256(+255712345678) → hash
    Actual numbers never leave the device

  Step 2: Hashes uploaded to NexGate server
    Not real phone numbers
    Server cannot reverse hashes

  Step 3: Server matches hashes
    Against registered users who have
    "Let others find me via phone number" = ON
    Returns: which hashes are NexGate users

  Step 4: App shows matched users
    "3 of your contacts are on NexGate"
    [Connect] buttons shown

  Non-NexGate contacts:
    Their hashes deleted from server immediately
    Never stored
    Never tracked
```

### First Launch Prompt

```
Shown once at first launch after onboarding:

┌──────────────────────────────────────────────┐
│ 📱 Pata Marafiki Wako NexGate               │
│                                              │
│ Ruhusu NexGate kutumia contacts zako        │
│ kupata marafiki wanaotumia NexGate tayari   │
│                                              │
│ ✓ Contacts zako hazitashirikiwa na mtu      │
│ ✓ Unaweza kufuta ruhusa wakati wowote       │
│ ✓ Nambari za simu zinabadilishwa kuwa       │
│   msimbo kabla ya kupakiwa                  │
│                                              │
│ [Ruhusu Contacts]    [Sasa Hivi Sio]        │
└──────────────────────────────────────────────┘

If declined:
  App works fully ✅
  Remind once after 7 days
  Never prompt again after second decline
  Always accessible in Settings
```

### Sync Scenarios

```
Scenario 1 — User accepts sync:
  Hashes uploaded
  Matched users shown
  User can message/follow matched users
  Sync runs again when contacts change

Scenario 2 — User declines sync:
  App works fully
  Can still find people via username/QR/VP Feed
  No contacts uploaded

Scenario 3 — User accepts then disables:
  Settings → Privacy → Contact Sync → OFF
  All hash data deleted from server
  Matched contacts remain as connections
  (connections not removed — only future sync stops)

Scenario 4 — User wants to delete contact data:
  Settings → Privacy → Delete Contacts Data
  All hashes deleted from server immediately
  Confirmation shown: "Contacts data imefutwa"

Scenario 5 — Contact not on NexGate:
  Their hash deleted from server
  User sees option: "Invite Juma to NexGate"
  Tap → share NexGate invite link via SMS/WhatsApp
```

---

## 4. Inbox Model

### Two Separate Inboxes

```
Every NexGate user has TWO completely separate inboxes:

Personal Inbox:
  1:1 DMs with other users (personal)
  Group chats (personal)
  Friend conversations
  Private — only the account owner sees this
  NEVER accessible to shop staff

Shop Inbox (one per shop owned):
  All customer conversations for that shop
  Commerce DMs with buyers
  Shared with authorized shop staff
  Completely separate from personal inbox
  Customer sees shop identity — not personal name
```

### Inbox Tabs

```
User with 2 shops sees:

┌─────────────────────────────────────────────┐
│  💬 Inbox                                   │
│                                             │
│  [Personal ●3] [TechStore ●12] [ClothingHub]│
│                                             │
│  Personal tab:                              │
│    Regular DMs + group chats                │
│    Private — owner only                     │
│                                             │
│  TechStore tab:                             │
│    Customer commerce conversations          │
│    Shared with assigned staff               │
│                                             │
│  ClothingHub tab:                           │
│    Separate shop conversations              │
│    Different staff assigned                 │
└─────────────────────────────────────────────┘

Staff member (Amina assigned to TechStore):
┌─────────────────────────────────────────────┐
│  [Personal] [TechStore ●12]                 │
│                                             │
│  ClothingHub: NOT visible (no access)       │
│  Owner personal: NEVER visible              │
└─────────────────────────────────────────────┘
```

### Conversation Types

```
Type            Owner           Participants
────────────────────────────────────────────────────
DM              USER            2 users
GROUP           USER            3-500 users
COMMERCE        SHOP            1 shop + 1 buyer
BROADCAST       USER or SHOP    1 sender + N followers
```

---

## 5. Messaging Permissions

### Who Can Message Who

```
Relationship                    Can message?    How
────────────────────────────────────────────────────────────
Saved contacts (phone)          ✅ Always       Direct
Mutual followers (VP Feed)      ✅ Always       Direct
Commerce relationship           ✅ Always       Direct (shop inbox)
Group chat members              ✅ Always       Via group only
Strangers (no relationship)     ⚠️  Request     Message request first
Blocked users                   ❌ Never        Blocked
```

### Message Request System

```
Stranger (no relationship) wants to message you:

  They can send ONE message request:
  ┌─────────────────────────────────────────┐
  │ 📩 Maombi ya Ujumbe                     │
  │                                         │
  │ Juma Mwangi anataka kukutumia ujumbe    │
  │ @juma_mwangi · 245 wafuasi              │
  │                                         │
  │ "Habari, nilikuona kwenye VP Shop..."   │
  │                                         │
  │ [Kubali]  [Kataa]  [Zuia]              │
  └─────────────────────────────────────────┘

If Kubali (Accept):
  Full conversation opens
  Juma can now message freely
  Can call after conversation established

If Kataa (Decline):
  Juma cannot send another request
  One attempt only per account
  Juma sees: "Ujumbe haukukubaliwa"

If Zuia (Block):
  Juma blocked immediately
  Cannot contact again in any way
  Juma sees: nothing (as if you don't exist)

If Ignored (no response):
  Request stays in request folder
  Juma cannot send more messages
  No call attempt allowed
  Request expires after 30 days
```

### Message Request Limits

```
Per account per day:
  Max 10 message requests sent
  Prevents spam

Per account to same person:
  1 request only (ever)
  If declined → cannot request again

Request folder:
  Separate from main inbox
  User checks it voluntarily
  No notification for low-priority requesters
    (unless they have mutual connections)
```

---

## 6. Call Permissions

### Permission Tiers

```
Tier                           Voice call    Video call
────────────────────────────────────────────────────────
Saved contacts                 ✅            ✅
Mutual followers               ✅            ✅
Commerce relationship          ✅ (shop)     ✅ (shop)
Active conversation partner    ✅            ✅
  (message request accepted)
Group chat member              ✅ (group)    ✅ (group)
Strangers                      ❌            ❌
Blocked users                  ❌            ❌
```

### Commerce Call Rules

```
Buyer can call:
  The SHOP JID — not the owner personally
  techstore@shops.nexgate.com
  Any available staff answers as TechStore

Seller (shop) can call:
  Buyers they have active commerce DM with
  Call goes from shop JID to buyer
  Buyer sees "TechStore" calling — not a personal name

Protecting personal contact:
  Owner's personal phone/JID never revealed
  Commerce calls always through shop identity
  Staff cannot reveal personal number to customers
```

### Anti-Harassment Call Rules

```
Call rate limiting:
  Max 3 unanswered calls to same person in 24 hours
  4th attempt → blocked for 24 hours automatically
  Resets daily

Silence unknown callers:
  Setting: "Pumzisha simu kutoka kwa wasio contacts"
  Calls from non-contacts ring silently
  Notification appears after — not during

First call warning:
  First EVER call from this person:
  ┌───────────────────────────────────────┐
  │ 📞 Simu ya kwanza kutoka              │
  │    Juma Mwangi (@juma_mwangi)        │
  │                                       │
  │ [Jibu]  [Kataa]  [Zuia]             │
  └───────────────────────────────────────┘

Quick block during call:
  Report button visible during call
  One tap → ends call + reports + blocks
```

### Privacy Settings for Calls

```
Who can call me:
  ○ Mutual followers + contacts (default)
  ○ My contacts only
  ○ Nobody

User can change anytime in Settings → Privacy → Calls
```

### Call Permission Check Flow

```
Kibuti tries to call Juma:
       │
Spring Boot permission check:
  Is Juma blocked by Kibuti? ❌ → reject immediately
  Is Kibuti blocked by Juma? ❌ → reject immediately
  Are they saved contacts? ✅ → allow
  Are they mutual followers? ✅ → allow
  Active commerce relationship? ✅ → allow
  Active accepted conversation? ✅ → allow
  Does Juma's privacy setting allow? ✅ → allow
  None of above → ❌ reject
       │
If rejected:
  App shows: "Huwezi kupiga simu mtu huyu"
  No Jingle stanza sent
  No TURN credentials issued

If allowed:
  TURN credentials generated
  Jingle session-initiate sent via Ejabberd
  Juma's device rings
```

---

## 7. 1:1 Messaging Flows

### Send Text Message

```
Happy path:
  User types message
  Taps send
  Message shows: pending ⏳
  Server receives → single tick ✓
  Recipient receives → double tick ✓✓
  Recipient reads → blue tick ✓✓

Offline path:
  User taps send
  Single tick ✓ (server received)
  Recipient offline → queued
  FCM push fires to recipient device
  Recipient comes online → message delivered
  Double tick ✓✓ appears
```

### Send Voice Note

```
User holds mic button:
  "Kibuti anarekodia..." shown to recipient (if online)
  Recording in progress
  Timer shown: 0:01, 0:02...

User releases:
  Voice note sent
  Waveform shown in thread
  Duration shown: 0:15

User swipes up while holding:
  Lock mode — records without holding
  Release sends

User swipes left while holding:
  Cancel recording — nothing sent

Limits:
  Basic users: max 2 minutes
  Pro users: max 15 minutes
  File size: max 16MB
```

### Send Media

```
Images:
  Max 10 images per message
  Formats: JPG, PNG, WebP, HEIC
  Max 16MB per image
  Auto-compressed for EA networks

Videos:
  Max 1 video per message
  Max 2 minutes (short video)
  Max 64MB
  Auto-transcoded to HLS by File Thunder

Files/Documents:
  Max 100MB per file
  Formats: PDF, DOCX, XLSX, ZIP, etc
  ClamAV scanned before delivery
  Available for 30 days then archived
```

---

## 8. Group Chat Flows

### Create Group

```
User creates group:
  Tap new group
  Add members (min 2, max 500)
  Set group name (required)
  Set group photo (optional)
  Tap create

Group created:
  Members notified: "Kibuti amekuongeza kwenye kikundi"
  All members see welcome message
  Creator is automatically OWNER
```

### Group Roles & Permissions

```
Role         Add members  Remove  Send msg  Delete msg  Change info
──────────────────────────────────────────────────────────────────
OWNER        ✅           ✅      ✅        Any msg ✅  ✅
ADMIN        ✅           ✅      ✅        Any msg ✅  ✅
MEMBER       ❌           ❌      ✅        Own only ✅ ❌
```

### Group Size Limits

```
Free group:    max 50 members
Pro group:     max 500 members

At 500 members:
  Consider using Broadcast Channel instead
  Broadcast = one way (better for large audiences)
  Group = two way (better for community)
```

### Leave / Remove

```
Member leaves:
  "Juma ameacha kikundi" shown in thread
  No longer receives messages

Admin removes member:
  "Amina ameondolewa na Kibuti" shown
  Member notified: "Umeondolewa"

Deleted group (owner only):
  All members removed
  All messages deleted (for everyone)
  Cannot be undone
```

---

## 9. Commerce DM Flows

### Flow 1 — Buyer Initiates from Product Page

```
Step 1: Buyer on product page
  Sees: [Chat na Muuzaji] button
  Tap button

Step 2: Conversation opens
  New DM opens OR existing conversation if already had one
  Conversation type: COMMERCE
  Conversation owner: SHOP (not personal)
  Product card auto-appears:
  ┌──────────────────────────────────────┐
  │ 📦 Samsung A15                       │
  │ TZS 450,000                          │
  │ Inapatikana: Vipande 12              │
  │ TechStore                            │
  │ [Jibu] [Angalia Bidhaa]             │
  └──────────────────────────────────────┘

  NOTE: Price in card is frozen at this moment
  Public price changes later → does NOT affect this card

Step 3: Negotiation
  Buyer and seller exchange text messages
  Normal conversation — no restrictions on text

Step 4: Agreement reached
  Seller taps attach (+) → From My Shop
  Selects product
  Sets custom price for THIS buyer ONLY
  Public product price: unchanged ✅
  Sends price offer card

Step 5: Buyer receives offer
  ┌──────────────────────────────────────┐
  │ 💰 Bei Maalum Kwako                  │
  │ Samsung A15                          │
  │ ~~TZS 450,000~~                      │
  │ TZS 400,000  (-TZS 50,000)          │
  │ Inaisha: dakika 28                   │
  │ Idadi: [─  1  +]                     │
  │ [Kataa]    [Endelea Kulipa →]       │
  └──────────────────────────────────────┘

Step 6: Buyer taps "Endelea Kulipa"
  Redirected to checkout flow (OUTSIDE inbox)
  Checkout at custom offer price
  Quantity confirmed in checkout

Step 7: Order placed
  Confirmation message appears in same thread:
  ┌──────────────────────────────────────┐
  │ ✅ Agizo Limethibitishwa             │
  │ Ord #ORD-XYZ-789                     │
  │ Samsung A15 × 1                      │
  │ TZS 400,000 imelipwa                 │
  │ [Fuatilia Agizo]                     │
  └──────────────────────────────────────┘
```

### Flow 2 — Seller Attaches from Inside Any Chat

```
Step 1: Seller inside any DM conversation
  Taps attach (+) button
  Menu appears:
    📷 Picha
    🎵 Sauti
    📄 Faili
    🏪 Kutoka Dukani  ← this one
    📅 Tukio
    👥 Ununuzi wa Pamoja

Step 2: Seller taps "Kutoka Dukani"
  Their shop product list opens
  Seller browses and selects product
  Sets custom price (optional)
  Sets quantity limit (optional)
  Adds note (optional): "Bei hii ni leo tu"
  Taps Send

Step 3: Buyer receives offer card
  Same offer card UI as Flow 1
  Same checkout redirect
  Same order confirmation in thread
```

### Commerce DM Rules

```
✅ Buyer can initiate from any product page
✅ Seller can attach from inside any conversation
✅ Custom price is private to this buyer only
✅ Public product price never changes
✅ Checkout always happens outside inbox
✅ Order confirmation appears in thread
✅ Multiple offers allowed in one conversation
❌ Buyer cannot request specific price (only negotiate via text)
❌ Offer cannot be edited after sending (send new one instead)
❌ Offer cannot be forwarded to other conversations
❌ Order confirmation cannot be deleted or edited
```

---

## 10. Offer Session Flows

### Offer Lifecycle

```
PENDING → offer sent, waiting for buyer response
       │
       ├──▶ DECLINED   buyer tapped "Kataa"
       │               seller notified
       │               seller can send new offer
       │
       ├──▶ EXPIRED    timer ran out
       │               both parties notified in thread
       │               seller can send new offer
       │
       ▼
ACCEPTED → buyer tapped "Endelea Kulipa"
       │    buyer enters checkout
       ▼
CHECKOUT → buyer in payment flow
       │
       ├──▶ ABANDONED  buyer left checkout without paying
       │               offer returns to PENDING? NO
       │               offer marked ABANDONED
       │               seller must send new offer
       │
       ▼
COMPLETED → order placed successfully
       │    cannot be reversed here (order system handles)
       ▼
CANCELLED → order cancelled (handled by order system)
            offer marked CANCELLED
```

### Multiple Offers — Same Conversation

```
Seller can send multiple offers:
  Each is independent session
  Previous offers remain in thread with their status

Thread shows history:
  ┌──────────────────────────────────────┐
  │ 💰 Bei Maalum: TZS 430,000          │
  │ ❌ Ilikataliwa                       │
  └──────────────────────────────────────┘

  ┌──────────────────────────────────────┐
  │ 💰 Bei Maalum: TZS 410,000          │
  │ ⏰ Imeisha muda                      │
  └──────────────────────────────────────┘

  ┌──────────────────────────────────────┐
  │ 💰 Bei Maalum: TZS 400,000          │
  │ ✅ Imekubaliwa                       │
  └──────────────────────────────────────┘
```

### Offer Expiry Scenarios

```
Scenario 1 — Expires while buyer is reading:
  Offer card shows live countdown timer
  Timer hits 0:00
  Card updates: "Imeisha muda"
  Proceed button disabled automatically
  Buyer sees: [Omba Bei Mpya] button

Scenario 2 — Expires while buyer in checkout:
  Buyer was on checkout page when offer expired
  Checkout validates offer at payment time
  If expired: payment rejected
  Buyer returned to conversation
  "Bei yako imeisha muda. Omba bei mpya"

Scenario 3 — Seller sends same price again:
  Allowed — new offer session created
  New 30-minute timer
  Both offers visible in thread (old + new)

Scenario 4 — Buyer tries to use expired offer link:
  Deep link from notification opens expired offer
  Shows: "Bei hii imeisha muda"
  [Rudi kwa Mazungumzo] button
```

### Price Security Rules

```
✅ Offer price stored server-side only
✅ Checkout validates price from server (not client)
✅ Client cannot manipulate price
✅ Offer is single-use (cannot complete twice)
✅ Offer belongs to specific buyer (others cannot use)
❌ Seller cannot change price after offer sent
   (must send new offer)
❌ Buyer cannot change price
❌ Public product price never affected
```

---

## 11. Shareable Content

### What Can Be Shared Into Any DM or Group

```
Content              Who can share    Restrictions
────────────────────────────────────────────────────────────
Text message         Anyone           None
Voice note           Anyone           Max 2 min (basic)
Image/Video          Anyone           Size limits
Product card         Anyone           Shows public price
Custom price offer   Seller only      Private — NOT forwardable
Event card           Anyone           None
Bei ya pamoja card   Anyone           None
VP Feed post         Anyone           None
VP Live stream       Anyone           None
Audio Space          Anyone           None
File/Document        Anyone           Max 100MB
```

### Forwarding Rules

```
Forward chain tracking:
  chain 1:    "↪ Imetumwa kutoka Juma Mwangi"
  chain 2-4:  "↪ Imetumwa"
  chain 5+:   "↪ Imetumwa mara nyingi"
              (misinformation warning — different icon)

Multi-forward limit:
  Max 5 conversations per forward action
  Prevents spam broadcasting

Cannot forward:
  Custom price offers (private deal)
  Order confirmations (private record)
  Payment confirmations (private record)
  System messages
```

---

## 12. Message Interactions

### Editing Messages

```
Rules:
  ✅ Only original sender can edit
  ✅ Text messages only (not media captions yet)
  ✅ Within 15 minutes of sending
  ✅ Shows "Imehaririwa" label after edit
  ✅ Original send time stays the same
  ✅ Works in group chats (sender edits their own)
  ❌ Commerce cards cannot be edited (immutable)
  ❌ System messages cannot be edited
  ❌ Voice notes cannot be edited
  ❌ After 15 minutes: edit option disappears

UI:
  Long press message → [Hariri] option (within 15 min)
  After edit: message updates in place
  "Imehaririwa" label appears below message
  Position in thread unchanged (no jump to bottom)
```

### Deleting Messages

```
Delete for Me:
  ✅ Any message, any time, no limit
  ✅ Only removed from your view
  ✅ Recipient still sees it
  ✅ Works on any message type

Delete for Everyone:
  ✅ Only original sender
  ✅ Within 15 minutes of sending
  ✅ Removed from all screens
  ✅ Shows: "Ujumbe huu umefutwa"
  ✅ Works in group chats
  ❌ Commerce cards: NOT allowed
  ❌ System messages: NOT allowed
  ❌ After 15 minutes: option disappears

Nothing is ever permanently deleted:
  Soft delete only
  For legal compliance + dispute resolution
  Admin can view deleted messages for support cases
  Users cannot recover deleted messages
```

### Reactions

```
Rules:
  ✅ Any message can be reacted to
  ✅ One reaction per user per message
  ✅ Change reaction: send new emoji (replaces old)
  ✅ Remove reaction: tap same emoji again
  ✅ Works on commerce cards (reactions don't modify content)
  ❌ System messages: no reactions

Available emojis at launch:
  ❤️  👍  😂  😮  😢  🙏

Expand to full emoji keyboard: Phase 3

Group reaction display:
  1 type:    "👍 3"
  2 types:   "👍 3  ❤️ 2"
  3+ types:  "👍 3  ❤️ 2  +2 zaidi"

Tap reaction to see who reacted:
  Bottom sheet opens
  List of names per emoji
```

### Replying to Messages

```
Rules:
  ✅ Anyone in conversation can reply to any message
  ✅ Reply shows quoted original above new message
  ✅ Tap quote → scrolls to original
  ✅ Works in group chats
  ✅ Works on any message type
  ❌ Cannot reply to deleted messages
     (shows: "Ujumbe umefutwa")

UI:
  Swipe right on message → reply mode
  OR long press → [Jibu] option
  Quote appears in input field
  Send normally
```

### Forwarding

```
Rules:
  ✅ Anyone can forward allowed content
  ✅ Max 5 conversations per forward action
  ✅ Forward chain tracked and shown
  ✅ Original sender name shown (chain 1 only)
  ✅ Media: references original file (no re-upload)
  ❌ Custom price offers: not forwardable
  ❌ Order/payment records: not forwardable
  ❌ System messages: not forwardable

UI:
  Long press message → [Tuma] option
  Conversation picker opens
  Select up to 5 conversations
  Tap send
```

---

## 13. Voice & Video Call Flows

### Initiating a Call

```
Kibuti taps call button on Juma's profile or conversation:
       │
Permission check:
  Relationship exists? ✅
  Juma's privacy allows? ✅
  Kibuti not blocked? ✅
       │
TURN credentials generated (server)
       │
Juma's phone rings:
  ┌──────────────────────────────────────┐
  │                                      │
  │        📞 Simu Inayoingia            │
  │                                      │
  │    [Kibuti Mwangi]                   │
  │    @kibuti                           │
  │                                      │
  │   [❌ Kataa]    [✅ Jibu]           │
  │                                      │
  └──────────────────────────────────────┘

Works even if:
  App is closed (FCM HIGH wakes it)
  Screen is locked (full screen notification)
  App is in background
```

### During a Call — Controls

```
Voice call controls:
  🔇 Mute/unmute microphone
  🔊 Speaker on/off
  📷 Enable camera (upgrade to video)
  ❌ End call

Video call controls:
  🔇 Mute/unmute microphone
  📷 Camera on/off
  🔄 Switch camera (front/rear)
  🖥️ Share screen
  🔊 Speaker on/off
  ❌ End call
```

### Switching Audio ↔ Video

```
Voice → Video:
  Kibuti taps camera button during voice call
  Juma sees: "Kibuti anataka kuongeza video"
  Auto-accepted (based on Juma's settings)
  OR Juma taps Accept
  Video starts — same call session continues
  Audio uninterrupted during upgrade

Video → Audio (manual):
  Kibuti taps camera OFF
  Video stops immediately for both
  Audio continues
  No renegotiation needed

Video → Audio (automatic):
  Network degrades below video threshold
  Video disabled automatically
  Banner shown: "Video imezimwa — mtandao dhaifu"
  Audio continues
  Video resumes when network improves
```

### Screen Sharing

```
Start screen share:
  Tap screen share icon during video call
  System permission dialog appears (Android/iOS)
  "Ruhusu NexGate kunasa skrini yako?"
  User accepts
  Screen share starts
  Other party sees your screen

During screen share:
  Your camera: small PiP (picture in picture)
  Their view: your screen (large) + your face (small)
  Your view: normal call view + "Unaonyesha skrini" banner

Stop screen share:
  Tap stop button
  Returns to normal video call
```

### Call Quality Indicators

```
Signal bars shown during call:
  ████  Excellent (WiFi / 4G strong)
  ███░  Good (4G)
  ██░░  Fair (3G) — may show quality banner
  █░░░  Poor (2G) — video disabled, audio only
  ░░░░  Very poor — "Mtandao dhaifu sana" banner

Quality banner examples:
  "Ubora wa sauti umepungua kwa sababu ya mtandao"
  "Video imezimwa — data ndogo"
  "Unaunganika tena..."
```

### Call End Scenarios

```
Normal end:
  One party taps end
  Other party sees call ended
  Duration shown in conversation: "Simu ya dakika 4:32"

Declined:
  Juma taps Kataa
  Kibuti sees: "Simu ilikataliwa"
  Missed call notification NOT sent (was declined)

No answer (timeout 45 seconds):
  Kibuti sees: "Hakujibu"
  Juma sees: "Simu iliyokosekana kutoka Kibuti" notification

Network failure:
  Both lose connection
  App attempts reconnect (10 seconds)
  If reconnect fails:
    Call marked as FAILED
    "Simu ilikatizwa" shown to both
    Kibuti can redial
```

---

## 14. Group Call Flows

### Starting a Group Call

```
From a group chat:
  Tap call icon in group header
  Choose: Voice only OR Video
  All group members receive incoming call notification
  Members who join → enter call
  Members who don't → miss it (missed call shown)

From a 1:1 conversation:
  Not supported directly
  Must create group first
  OR use "Add person" button during active 1:1 call

Add person to active 1:1 call:
  During call → tap "Ongeza Mtu"
  Pick from allowed contacts
  They receive group call invitation
  They join → 1:1 becomes group call
```

### Group Call Limits

```
Voice only group call:
  Max 8 participants (comfortable for 3G)
  Up to 12 possible but discouraged on EA networks

Video group call:
  Max 4 video feeds shown simultaneously
  5th person onwards: audio only tile shown
  Active speaker highlighted (larger tile)
  Tap any tile to pin/feature them

Layout options:
  Grid view: all tiles equal size
  Speaker view: active speaker large, others small
  Auto: switches based on who is talking
```

### Group Call Permission Rules

```
Within a group chat:
  Any member can start group call ✅
  Any member can join ✅
  No extra permission check needed
  (group membership = call permission)

Adding someone outside the group:
  Same call permission check as 1:1
  Must have relationship (contact/follower/commerce)
  If no relationship → cannot add
```

---

## 15. Offline & Notification Flows

### Message Notification Levels

```
Level        When                              FCM    SMS after
──────────────────────────────────────────────────────────────
CRITICAL     Order placed/paid/failed          ✅     0 min
             Delivery update
             Payment confirmation

IMPORTANT    Commerce DM from buyer            ✅     10 min
             Custom price offer received
             Bei ya pamoja threshold reached
             Call missed (commerce context)

NORMAL       Regular DM                        ✅     Never
             Group message
             Reaction
             Follow notification
```

### Offline Message Delivery

```
User offline — message sent to them:
       │
       ├── FCM HIGH priority push fired immediately
       │   (all message levels)
       │
       ├── CRITICAL: Textfy SMS sent simultaneously
       │
       ├── IMPORTANT: Textfy SMS after 10 min
       │   (if FCM not acknowledged)
       │
       └── Message queued in RabbitMQ

User comes back online:
       │
       ├── WS connects → presence registered
       ├── Queued messages drained (priority order)
       │   CRITICAL first → IMPORTANT → NORMAL
       │
       └── Catch-up banner shown:
           "Umekosa: maagizo 2, ujumbe 8"
           [Angalia Maagizo] [Ona Ujumbe]
```

### Textfy SMS Templates

```
Order placed (to seller):
  "NexGate: Agizo jipya kutoka [Buyer]!
   TZS [amount]. Kagua: nexgate.app/orders/[id]"

Payment received:
  "NexGate: Malipo ya TZS [amount]
   yamepokelewa kutoka [Buyer].
   nexgate.app/wallet"

Commerce DM (to seller):
  "NexGate: [Buyer] anakuuliza kuhusu
   [Product]. Jibu: nexgate.app/chat/[id]"

Bei ya pamoja:
  "NexGate: Watu [n]/[target] wamejiunga!
   nexgate.app/group-buy/[id]"

All SMS:
  Swahili first ✅
  Deep link included ✅
  Shop name shown (not staff name) ✅
```

---

## 16. Privacy & Safety

### Privacy Settings Matrix

```
Setting                      Options               Default
────────────────────────────────────────────────────────────────
Who can message me           Everyone / Followers  Followers
                             + Contacts / Contacts + Contacts
                             / Nobody

Who can call me              Followers+Contacts /  Contacts
                             Contacts / Nobody      only

Last seen                    Everyone / Contacts / Contacts
                             Nobody

Profile picture              Everyone / Contacts / Everyone
                             Nobody

Be found by phone number     On / Off              On

Contact sync                 On / Off              Off

Read receipts (blue ticks)   On / Off              On
  (off = others see delivered but not read)
```

### Block System

```
User A blocks User B:

  User B cannot:
    ❌ See User A's profile
    ❌ Send messages to User A
    ❌ Call User A
    ❌ See User A in search
    ❌ See User A's VP Feed posts
    ❌ See User A's online status

  User B sees:
    Profile: "Mtumiaji huyu hayupo"
    Messages: appear sent but never delivered

  User A can:
    ✅ Unblock anytime from Settings
    ✅ See User B's profile still (A blocked B — not reverse)

  Group chats:
    If in same group: messages visible but
    cannot DM or call each other directly
    Admin can remove either from group
```

### Report System

```
Report options:
  Spam
  Harassment / Vitisho
  Inappropriate content
  Fake account
  Scam / Udanganyifu
  Other

Report flow:
  Long press message → [Ripoti]
  OR profile → [...] → [Ripoti]
  Choose reason
  Optional: add description
  Submit

After report:
  User not notified they were reported
  Report goes to NexGate moderation queue
  Automatic temporary restrictions may apply
  for high-volume reporters

Block on report:
  "Ripoti na Zuia" option available
  Blocks immediately + sends report
```

### Safety for Women — Specific Features

```
This is important for EA platform trust:

Silence unknown callers:
  ON by default for new accounts
  Calls from non-contacts ring silently
  User sees missed call — no disruption

Call rate limiting:
  3 unanswered calls per day to same person
  Automatic block after 4th attempt

Quick block during call:
  One tap visible during any call
  Ends + reports + blocks in single action

Message request system:
  Strangers cannot freely DM
  Must send request first
  User controls who enters their inbox

Default privacy settings:
  New accounts: strict defaults
  Users open up if they choose
  Better to protect by default
```

---

## 17. Shop Inbox & Staff Access

### Shop Tiers

```
Basic shop (free):
  Owner manages inbox alone
  No staff assignment
  One active stream at a time
  Standard features

Pro shop (paid):
  Staff roles unlocked
  Multiple staff in inbox
  Advanced analytics
  Priority support
  Audit logs
```

### Staff Roles

```
Role            Inbox   Products  Analytics  Settings  Staff mgmt
──────────────────────────────────────────────────────────────────
OWNER           ✅      ✅        ✅         ✅        ✅
MANAGER         ✅      ✅        ✅         ❌        ❌
SUPPORT_AGENT   ✅      ❌        ❌         ❌        ❌
READ_ONLY       👁️ view ❌        ❌         ❌        ❌
```

### Staff Identity Rules

```
Staff responds to customer:
  Customer sees: "TechStore" (shop name)
  Customer NEVER sees: "Amina" (staff name)
  Staff identity is internal only

Audit log (internal):
  Which staff member responded
  Timestamp
  Visible to OWNER and MANAGER only
  Not visible to customers

Staff cannot:
  ❌ Reveal personal phone number to customers
  ❌ Switch conversation to personal DM
  ❌ Access owner's personal inbox
  ❌ Access other shops they're not assigned to
```

### Multiple Shops — Inbox Separation

```
Owner with 3 shops:
  Each shop = separate inbox tab
  Each shop = separate conversation records
  Each shop = separate staff assignments
  Staff for Shop A cannot see Shop B
  Owner sees all (as owner of all)

Commerce DM routing:
  Buyer messages about Product A (TechStore product)
  → Goes to TechStore inbox
  → NOT to ClothingHub inbox
  → NOT to owner's personal inbox
  Correctly routed by product ownership
```

---

## 18. Notification Settings

### Per-Conversation Settings

```
Each conversation has:
  Notifications:
    ○ All messages
    ○ Mentions only (groups)
    ○ Muted (until: 8 hours / 1 week / forever)

  Media auto-download:
    ○ WiFi only
    ○ WiFi + Mobile data
    ○ Never
```

### Global Notification Settings

```
Message notifications:   On / Off
Call notifications:      On / Off
Reaction notifications:  On / Off
Group notifications:     On / Off
Commerce notifications:  On / Off (cannot turn off CRITICAL)
Sound:                   Default / Custom / Silent
Vibration:               On / Off
In-app preview:          Show / Hide content
```

### CRITICAL Notifications — Always On

```
These cannot be turned off by user:
  Order placed (seller receives)
  Payment confirmed (buyer receives)
  Payment failed (buyer receives)
  Order cancelled (both receive)

Reason:
  Financial events
  User could miss critical money information
  Platform liability without guaranteed delivery
  Textfy SMS fallback ensures they always arrive
```

---

## 19. Edge Cases & Scenarios

### Messaging Edge Cases

```
User deletes their account:
  Their messages remain visible in conversations
  Name shows: "Mtumiaji aliyefuta akaunti"
  Profile picture: default avatar
  Cannot be messaged or called

User changes username:
  All conversations update automatically
  Old username links still work (redirect)
  No broken references

Very long message (>4000 chars):
  Truncated in thread: first 200 chars + "...Soma zaidi"
  Tap to expand full message
  Not split into multiple messages

Same message sent twice (duplicate):
  Detected by temp_id
  Only one stored in DB
  User sees single message (not duplicate)

Message sent to blocked user:
  Appears sent (single tick)
  Never delivered (stays at single tick)
  User not informed of block
```

### Commerce Edge Cases

```
Product goes out of stock while offer pending:
  Offer still valid (was frozen at send time)
  Checkout validates stock at payment time
  If out of stock at payment:
    Payment rejected
    "Bidhaa hii imeisha"
    Buyer returned to conversation
    Seller notified to resend offer or cancel

Product price changes while offer pending:
  Offer price locked at creation ✅
  Public price change does NOT affect offer
  Buyer pays the offer price always

Seller deletes product while offer pending:
  Offer still valid (snapshot frozen)
  Checkout validates product existence
  If product deleted at payment:
    Payment rejected
    "Bidhaa hii haipatikani tena"

Two buyers get offers for last item:
  Both offers exist simultaneously
  First to complete checkout gets the item
  Second buyer's payment rejected:
    "Bidhaa hii imeisha"
  Standard e-commerce race condition handling

Seller sends offer to wrong person:
  Cannot retract offer
  Offer expires naturally
  Seller can contact NexGate support if urgent

Buyer in checkout when offer expires:
  Payment attempt fails
  "Bei yako imeisha muda"
  Must request new offer from seller
```

### Call Edge Cases

```
Call drops mid-conversation:
  WebRTC detects loss
  Auto-reconnect attempted (10 seconds)
  If reconnect success: call continues
  If reconnect fails: call ended
  Both see: "Simu ilikatizwa"
  Kibuti can redial immediately

Both call each other simultaneously:
  Race condition — both see "Simu inayoingia"
  System picks one (first to reach server)
  Other cancelled automatically
  One call established

Call during active call:
  Second call: goes to missed calls
  Busy signal not sent (no UX for this)
  After first call: notification of missed call

Phone runs out of battery during call:
  WebRTC detects disconnect
  Same as network drop
  Other party sees: "Simu ilikatizwa"

Someone calls during Do Not Disturb (DND):
  DND on device: depends on device DND rules
  NexGate mute: call goes to missed calls silently
  CRITICAL commerce calls: bypass mute (configurable)
```

### Group Chat Edge Cases

```
Owner leaves group:
  Must transfer ownership first
  OR system auto-assigns to oldest admin
  OR if no admins: oldest member becomes owner

Last person leaves group:
  Group archived automatically
  Messages preserved for 30 days
  Then permanently deleted

Adding member who blocked you:
  Cannot add blocked users to groups
  System rejects silently
  No error shown (privacy)

Member blocked in group:
  They stay in group (cannot remove via block)
  Cannot DM each other
  Can both still see group messages
  Admin can remove either from group
```

---

## Summary

NexGate messaging is built for East African social commerce — where every
conversation can become a transaction, and every transaction has a conversation
behind it.

The permission system is designed to be safe by default: contacts and mutual
followers can communicate freely, strangers go through message requests,
and calls require an established relationship. Commerce contexts create
natural communication permissions — buyers and sellers can always reach
each other through shop identities that protect personal privacy.

Contact sync is optional, privacy-preserving (hashed on device), and
exists alongside multiple other discovery mechanisms. NexGate does not
need contact sync to function — it is one convenience feature among many.

Commerce offer sessions are the unique heart of NexGate messaging — a formal
negotiated deal between a specific buyer and seller, with a locked price,
expiry timer, and full lifecycle tracking. The public product price is
never affected. Checkout always happens outside the inbox. Everything is
immutable for legal and dispute reasons.

The safety system protects users (especially women) through strict defaults,
rate limiting, quick block options, and a message request system that gives
users control over who enters their inbox.

---

*NexGate Messaging — Product Requirements & Feature Flows v1.0*
*QBIT SPARK | Rules · Flows · Permissions · Scenarios · Edge Cases*