Phase 3.2 WS1: Chat request overlay, queue, stale reasons

- Backend: add reason field to chat_request_closed WS messages
  (cancelled_by_customer, accepted_by_other, expired)
- Backend: include duration_minutes, is_free_trial in chat_request WS
- ChatRequestNotifier: add ChatRequestStaleData, StaleReason enum,
  request queue (List<Map>), ignore(), acknowledgeStale(), _advanceQueue()
- New ChatRequestOverlay widget: slides up from bottom, dimmed background,
  swipe to dismiss, shows active/stale request content
- Integrate overlay in main.dart wrapping MaterialApp.router
- Cleanup: convert HomeScreen to ConsumerWidget, remove showModalBottomSheet,
  remove IncomingRequestSheet, remove lifecycle observer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 22:16:30 +08:00
parent 4158fb9432
commit b9c4841eb1
7 changed files with 380 additions and 175 deletions

View File

@@ -101,6 +101,8 @@ export const createPairingRequest = async (customerId, { duration_minutes, price
type: WsMessage.CHAT_REQUEST,
session_id: session.id,
created_at: session.created_at,
duration_minutes: session.duration_minutes,
is_free_trial: session.is_free_trial,
})
}
@@ -202,6 +204,7 @@ export const acceptPairingRequest = async (sessionId, mitraId) => {
await notifyMitra(n.mitra_id, {
type: WsMessage.CHAT_REQUEST_CLOSED,
session_id: sessionId,
reason: 'accepted_by_other',
})
}
@@ -245,7 +248,7 @@ export const cancelPairingRequest = async (sessionId, customerId) => {
WHERE session_id = ${sessionId} AND response IS NULL
`
// Notify mitras to dismiss
// Notify mitras to dismiss (customer cancelled)
const notifications = await sql`
SELECT mitra_id FROM chat_request_notifications WHERE session_id = ${sessionId}
`
@@ -253,6 +256,7 @@ export const cancelPairingRequest = async (sessionId, customerId) => {
await notifyMitra(n.mitra_id, {
type: WsMessage.CHAT_REQUEST_CLOSED,
session_id: sessionId,
reason: 'cancelled_by_customer',
})
}
@@ -283,7 +287,7 @@ export const expirePairingRequest = async (sessionId) => {
session_id: sessionId,
})
// Notify mitras to dismiss
// Notify mitras to dismiss (request expired)
const notifications = await sql`
SELECT mitra_id FROM chat_request_notifications WHERE session_id = ${sessionId}
`
@@ -291,6 +295,7 @@ export const expirePairingRequest = async (sessionId) => {
await notifyMitra(n.mitra_id, {
type: WsMessage.CHAT_REQUEST_CLOSED,
session_id: sessionId,
reason: 'expired',
})
}