Skip to content

Commit 96d0fea

Browse files
committed
Keep inbox open after email send
1 parent f0a80a1 commit 96d0fea

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

packages/server/src/index.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,45 @@ app.post('/api/mock/email-monitor/start', (req, res) => {
604604
res.json({ ok: true, started: true, sessionId })
605605
})
606606

607+
app.post('/api/sessions/:id/email-send', async (req, res) => {
608+
const session = getSession(req.params.id)
609+
if (!session) return res.status(404).json({ error: 'Session not found' })
610+
611+
const payload = session.payload as Record<string, unknown>
612+
const inbox = Array.isArray(payload.inbox) ? payload.inbox as Array<Record<string, unknown>> : []
613+
const emailId = typeof req.body?.emailId === 'string' ? req.body.emailId : ''
614+
if (!emailId) return res.status(400).json({ error: 'Missing emailId' })
615+
616+
const nextInbox = inbox.filter(email => String(email.id ?? '') !== emailId)
617+
const result = req.body && typeof req.body === 'object' ? req.body as Record<string, unknown> : {}
618+
619+
updateSessionPayload(req.params.id, {
620+
...payload,
621+
inbox: nextInbox,
622+
})
623+
updateSessionPageStatus(req.params.id, { state: 'submitted', updatedAt: Date.now() })
624+
625+
let callbackFailed = false
626+
let callbackError = ''
627+
if (session.sessionKey) {
628+
try {
629+
const lines = ['[agentclick] User sent an email reply from inbox review.']
630+
const editedDraft = result.editedDraft && typeof result.editedDraft === 'object'
631+
? result.editedDraft as Record<string, unknown>
632+
: null
633+
const subject = typeof editedDraft?.subject === 'string' ? editedDraft.subject : ''
634+
if (subject) lines.push(`- Subject: ${subject}`)
635+
lines.push(`- Removed email ${emailId} from inbox UI after send.`)
636+
await callWebhook({ message: lines.join('\n'), sessionKey: session.sessionKey, deliver: true })
637+
} catch (err) {
638+
callbackFailed = true
639+
callbackError = String(err)
640+
}
641+
}
642+
643+
res.json({ ok: true, callbackFailed, callbackError, remaining: nextInbox.length })
644+
})
645+
607646
app.get('/api/sessions/:id/summary', (req, res) => {
608647
const session = getSession(req.params.id)
609648
if (!session) return res.status(404).json({ error: 'Session not found' })

packages/web/src/pages/EmailTestPage.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ Acme Cloud Billing`,
2626
{ label: 'Thread', value: 'March Billing Summary' },
2727
],
2828
unread: true,
29-
replyState: 'loading',
3029
category: 'Updates',
3130
timestamp: Date.now() - 1000 * 60 * 45,
3231
},

packages/web/src/pages/ReviewPage.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,57 @@ export default function ReviewPage() {
543543
const editedDraftParagraphs = hasInbox
544544
? buildEditedParagraphs(activeDraftForState?.paragraphs ?? [], states, rewriteInput)
545545
: []
546+
if (hasInbox && confirmed && selectedEmailId) {
547+
const body = {
548+
actions,
549+
confirmed: true,
550+
markedAsRead,
551+
markedAsReadDetails: (payload as InboxPayload).inbox
552+
.filter(email => markedAsRead.includes(email.id))
553+
.map(email => ({ id: email.id, from: email.from, subject: email.subject })),
554+
userIntention,
555+
selectedIntents: selectedIntentsList,
556+
unreadEmailCount: (payload as InboxPayload).inbox.filter(email => email.unread !== false && !markedAsRead.includes(email.id)).length,
557+
editedDraft: {
558+
emailId: selectedEmailId,
559+
to: draftTo,
560+
cc: draftCc,
561+
bcc: draftBcc,
562+
subject: draftSubject,
563+
body: paragraphsToText(editedDraftParagraphs),
564+
paragraphs: editedDraftParagraphs,
565+
},
566+
pageStatus: {
567+
state: 'submitted',
568+
},
569+
}
570+
const result = await fetch(`/api/sessions/${id}/email-send`, {
571+
method: 'POST',
572+
headers: { 'Content-Type': 'application/json' },
573+
body: JSON.stringify({
574+
emailId: selectedEmailId,
575+
...body,
576+
}),
577+
}).then(r => r.json())
578+
579+
if (result.callbackFailed) {
580+
setCallbackFailed(true)
581+
}
582+
583+
setPayload(currentPayload => {
584+
if (!currentPayload || !('inbox' in currentPayload)) return currentPayload
585+
return {
586+
...currentPayload,
587+
inbox: currentPayload.inbox.filter(email => email.id !== selectedEmailId),
588+
}
589+
})
590+
setMarkedAsRead(current => Array.from(new Set([...current, selectedEmailId])))
591+
setSelectedEmailId(null)
592+
setRightView('empty')
593+
resetEditState()
594+
setSubmitting(false)
595+
return
596+
}
546597
const body = hasInbox
547598
? JSON.stringify({
548599
actions,

0 commit comments

Comments
 (0)