feat(demo): P3.5 inbox sidebar + inbox smoke (P3 complete)

message-demo gains a per-person Inbox rail (useInbox, done/snooze); sending
surfaces a NEEDS_REPLY item in the other pane; reading auto-resolves it.
smoke-inbox.mjs proves the event-driven flow end-to-end (send -> relay ->
projector -> inbox -> read -> DONE). Realtime smoke still passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 07:24:52 +05:30
parent 0a1631dbc0
commit 942f40292e
4 changed files with 96 additions and 3 deletions
+1
View File
@@ -10,6 +10,7 @@
"dependencies": {
"@insignia/iios-kernel-client": "workspace:*",
"@insignia/iios-message-web": "workspace:*",
"@insignia/iios-inbox-web": "workspace:*",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
+27 -3
View File
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { MessageProvider, useThread, useMessages } from '@insignia/iios-message-web';
import { InboxProvider, useInbox } from '@insignia/iios-inbox-web';
const SERVICE = 'http://localhost:3200';
const APP_ID = 'portal-demo';
@@ -83,12 +84,35 @@ function ChatInner({
);
}
function InboxSidebar() {
const { items, done, snooze } = useInbox({ state: 'OPEN' });
return (
<div style={{ width: 170, borderRight: '1px solid #ddd', padding: 8, fontSize: 12, fontFamily: 'sans-serif' }}>
<b>Inbox ({items.length})</b>
{items.length === 0 && <div style={{ color: '#999', marginTop: 6 }}>nothing to reply to</div>}
{items.map((i) => (
<div key={i.id} style={{ padding: '6px 0', borderBottom: '1px solid #eee' }}>
<div style={{ fontWeight: 600 }}>{i.title}</div>
<div style={{ color: '#999' }}>{i.kind}</div>
<button style={{ marginRight: 4 }} onClick={() => void done(i.id)}>done</button>
<button onClick={() => void snooze(i.id)}>snooze</button>
</div>
))}
</div>
);
}
function Pane(props: { token: string | null; label: string; threadId: string | null; onCreated?: (id: string) => void }) {
if (!props.token) return <div style={{ flex: 1, margin: 8 }}>loading {props.label}</div>;
return (
<MessageProvider serviceUrl={SERVICE} token={props.token}>
<ChatInner label={props.label} threadId={props.threadId} onCreated={props.onCreated} />
</MessageProvider>
<div style={{ flex: 1, display: 'flex', margin: 8, border: '1px solid #ccc', borderRadius: 8, overflow: 'hidden' }}>
<InboxProvider serviceUrl={SERVICE} token={props.token}>
<InboxSidebar />
</InboxProvider>
<MessageProvider serviceUrl={SERVICE} token={props.token}>
<ChatInner label={props.label} threadId={props.threadId} onCreated={props.onCreated} />
</MessageProvider>
</div>
);
}