@noorden/conversations-react
v0.1.0
Published
Inbox de conversas embarcável da Noorden Platform — headless hooks + componentes React estilizados.
Maintainers
Readme
@noorden/conversations-react
Inbox de conversas embarcável da Noorden Platform, no modelo Stream/Sendbird UIKit:
headless hooks (toda a lógica) + componentes estilizados opcionais construídos
sobre os hooks. Peer dep: react >= 18.
npm install @noorden/conversations-reactAuth: token efêmero (a API key NUNCA vai ao browser)
O kit se autentica com um token curto (rxct_…, TTL ~15 min) restrito às rotas de
conversas/contatos de UM projeto. O seu servidor troca a API key pelo token:
// No SEU backend (ex.: rota /api/inbox-token), com @noorden/sdk:
import { Platform } from '@noorden/sdk';
const platform = new Platform({ apiKey: process.env.NOORDEN_API_KEY });
export async function GET() {
const { token } = await platform.conversations.createInboxToken();
return Response.json({ token });
}// No browser:
import { InboxClient, InboxProvider, Inbox } from '@noorden/conversations-react';
import '@noorden/conversations-react/styles.css';
const client = new InboxClient({
baseUrl: 'https://SUA-API',
projectId: 'proj_…',
getToken: async () => {
const res = await fetch('/api/inbox-token'); // seu backend
const { token } = await res.json();
return token;
},
});
export function SupportPage() {
return (
<InboxProvider client={client}>
<div style={{ height: 640 }}>
<Inbox senderName="Equipe de Suporte" />
</div>
</InboxProvider>
);
}O client cacheia o token e chama getToken de novo quando a API responde 401 (expirou)
— renovação transparente. Revogar/rotacionar a API key emissora mata todos os tokens.
Contrato público (0.1.0, semver)
Headless hooks (não dependem dos componentes nem do CSS)
| hook | retorno | live |
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------- |
| useConversations({ status?, controller?, channel?, connectionId?, contactId?, limit?, pollIntervalMs?, client? }) | { conversations, loading, error, refresh, loadMore, hasMore, loadingMore } | poll 10s |
| useConversation(id, { pollIntervalMs?, client? }) | { conversation, loading, error, refresh } | poll 10s |
| useMessages(id, { limit?, pollIntervalMs?, client? }) | { messages (cronológicas), loading, error, refresh, loadMore, hasMore, loadingMore } | poll 3s |
| useSendMessage(id, { senderName?, client? }) | { send(text), sending, error } — envia como user (takeover) | — |
| useContact(contactId, { pollIntervalMs?, enabled?, client? }) | { contact, loading, error, refresh } | poll 30s |
Regras dos hooks: poll nunca "pisca" o loading (mantém os dados anteriores); erro
transitório não apaga a lista; pollIntervalMs: 0 desliga o poll; client explícito
dispensa o <InboxProvider>.
Atualização ao vivo = polling (débito assumido). A API de conversations não expõe SSE para operador hoje; quando expuser, os hooks trocam o intervalo por stream sem mudar este contrato.
Componentes estilizados (opcionais, construídos SOBRE os hooks)
<Inbox title? filters? pollIntervalMs? contactPanel? senderName? onSelectConversation? client? />— lista + thread + painel do contato (3 colunas).<Thread conversationId pollIntervalMs? senderName? composer? client? />— mensagens públicas em ordem cronológica + composer embutido.<Composer conversationId senderName? placeholder? onSent? client? /><ContactPanel conversationId client? />
CSS opcional em @noorden/conversations-react/styles.css — classes prefixadas
(nrdc-*) e theming por CSS vars (--nrdc-primary, --nrdc-surface, …). Sem o CSS, os
componentes renderizam markup semântico sem estilo (ou ignore os componentes e use só os
hooks).
Notas
- O thread mostra apenas mensagens
visibility: 'public'(rascunhos de copiloto são fluxo do console). - Envio é sempre
senderType: 'user'— numa conversa controlada por agente, isso faz o takeover (comportamento padrão da plataforma). - Janela de 24h fechada → o envio falha com
conversation.window_closed(o erro aparece no composer).
