Skip to main content

Requests Live

Community Q&A. Users post requests for help, services, or items. Two discussion modes — private 1-to-1 supporter chats (default) or public many-to-many community discussions.

1. Overview

A consumer posts a request ("Need a plumber in Gadag", "Looking for organic vegetables", "Has anyone tried this restaurant?"). Other users respond. Two modes:

  • Private (default): up to 3 supporters, each gets their own 1-to-1 thread with the requester
  • Public: one shared discussion thread, anyone can join, no cap

The mode is set at post-time via a toggle in the create form. After creation it can't be changed.

2. User journey

Posting

  1. Requests tab → "+ Post"
  2. Title + description (voice input supported in 12 Indian languages via Sarvam AI)
  3. Category (35 options: Driver, Plumber, Mechanic, etc., searchable, custom entries allowed)
  4. Toggles: Public discussion (Y/N), Anonymous (Y/N)
  5. Optional phone (hidden if anonymous)
  6. Submit → notifies nearby service providers + community members

Responding (private mode)

  1. Browse requests → tap one
  2. Tap "I Can Help" → creates a 1-to-1 thread with the requester
  3. Chat in real-time
  4. Up to 3 helpers per request

Joining (public mode)

  1. Browse requests → tap one with PUBLIC badge
  2. Tap "Open Discussion" / "Join Discussion" → enters the shared thread
  3. See messages from all participants in chronological order
  4. Anyone can post; messages can be edited within 15 min (mobile only at the moment)

Owner actions

  • View threads (or shared discussion)
  • Mark Complete (closes the request)
  • Delete

3. Web ↔ Mobile parity

CapabilityWebMobile
Post request✅ With public/private toggle✅ Same toggle
Browse with category filter
"My Requests" filter
"I Helped" filter
Public/Private filter pills❌ Not yet
PUBLIC badge on cards✅ Added 2026-04-17
"Open Discussion" CTA✅ Added 2026-04-17
Edit/delete own messages✅ (15-min window)
Member count on public threads❌ Just shows "Public Discussion"✅ "Public Discussion · N members"

4. Key components

Web

  • src/app/(consumer)/requests/page.tsx — list view + grid card + detail modal + create modal + thread chat (single file, ~1850 lines)

Mobile

  • mobile/app/(tabs)/requests.tsx — list + detail + chat + post flow (single file, ~3500 lines)

Shared

  • src/app/api/requests/** — CRUD + threads + messages

5. APIs

EndpointMethodPurpose
/api/requestsGETList with filters (category, mine=true, helped=true, status)
/api/requestsPOSTCreate. Body includes discussionType: "public" | "private"
/api/requests/[id]GET, PATCH, DELETESingle request — owner can update status / delete
/api/requests/[id]/threadsGET, POSTList threads (public mode → returns single shared thread; private mode → list of supporter threads, max 3)
/api/requests/[id]/threads/[threadId]/messagesGET, POSTChat messages
/api/requests/[id]/threads/[threadId]/messages/[messageId]PATCH, DELETEEdit / soft-delete (sender only, 15-min window for public)

6. Database touchpoints

  • RequestdiscussionType"private" \| "public" (default "private"), status, consumerId, category, phone, isAnonymous, geolocation
  • RequestThread — for private mode there's one row per supporter (max 3); for public mode there's exactly one row (supporterId is set to the request owner as a schema compromise — UI never surfaces it)
  • RequestMessage — chat. editedAt, isEdited, isDeleted fields support edit/delete

7. Business logic

Private vs Public branching (the gnarly bit)

The /api/requests/[id]/threads POST endpoint detects mode and takes different branches:

  • private → creates a new thread for the supporter (rejects if MAX_THREADS_PER_REQUEST=3 reached, rejects owner)
  • public → idempotent lookup-or-create of the single shared thread, no owner rejection, no max-threads check

/api/requests/[id]/threads/[threadId]/messages GET/POST gates the 403 on !isPublic so anyone can read/post in public threads.

This was bug #1 in the 2026-04-15 mobile fixes — the original endpoint unconditionally rejected the owner with "you cannot support your own request", which broke "Open Discussion" for the requester.

Anti-spam measures

  • Self-support prevention (private mode): owner can't open a thread on their own request
  • Phone optional, hidden when isAnonymous
  • 35 categories with controlled vocabulary; custom categories tracked separately
  • Auto-message: tapping "I Can Help" creates the thread with a first message — no empty threads

Public-thread member count

Computed on read by counting distinct consumerId in RequestMessage for that thread, plus the request owner. Mobile displays it; web doesn't (yet).

8. Feature flags / env vars

  • None gating this feature.
  • Voice input requires SARVAM_API_KEY.

9. Tests

  • tests/web-mobile-parity-requests.test.ts — 9 tests locking the public-discussion UI parity (added 2026-04-17 after the bug)
  • tests/public-discussion.test.ts — public-mode branching (currently fails on import due to missing GIPHY route — pre-existing, unrelated)
  • tests/mobile-bugs-2026-04-15.test.ts — 4 mobile bug fixes including the public-mode threads endpoint

10. Known gotchas

  • RequestThread.supporterId is set to the OWNER for public-mode threads — schema compromise to avoid a migration. The public UI never reads this field, so it's invisible. Don't be surprised if you see thread.supporterId === request.consumerId on public threads.
  • Web doesn't have edit/delete for messages. Mobile does (with a 15-min window). Don't write a feature that depends on web-side edit until we add it.
  • Voice input is best-effort. If the user records but Sarvam fails, the form still submits with whatever text was typed. Don't block submit on STT.
  • Public discussions don't show in "My Requests" filter unless you posted them. The filter is purely owner-based, not member-based.