Menu de guias

React Native

O pacote @kavchat/react-native adiciona ao seu app o chat de atendimento baseado em roteiros: um Provider, uma tela de chat e um botão flutuante opcional. As respostas vêm dos roteiros que você publicou no painel e são executadas no dispositivo.

Instalação

O pacote é escrito inteiramente em JavaScript, sem código nativo próprio e sem biblioteca de ícones. Por isso, funciona da mesma forma em um app React Native comum e no Expo. Dependências de pares: react a partir da versão 18 e react-native a partir da versão 0.72.

React Native CLI (sem Expo)

Terminal
yarn add @kavchat/react-native @react-native-async-storage/async-storage
cd ios && pod install

AsyncStorage é um módulo nativo: pod install faz a vinculação no iOS, e no Android a vinculação é automática.

Expo

Funciona no Expo Go e em builds de desenvolvimento:

Terminal
yarn add @kavchat/react-native
npx expo install @react-native-async-storage/async-storage

Sem AsyncStorage

O AsyncStorage mantém a versão publicada e a conversa entre as inicializações. A instalação é opcional: sem ele, o pacote mantém tudo na memória. O bot funciona, mas baixa a versão novamente a cada inicialização. Para usar outro armazenamento, como MMKV, passe na prop storage qualquer objeto { getItem, setItem, removeItem? }, síncrono ou assíncrono:

TSX
import { MMKV } from 'react-native-mmkv';
const mmkv = new MMKV();
const storage = {
  getItem: (k: string) => mmkv.getString(k) ?? null,
  setItem: (k: string, v: string) => mmkv.set(k, v),
  removeItem: (k: string) => mmkv.delete(k),
};

<KavchatProvider publicKey="pk_live_..." storage={storage}>

Uso

TSX
import { KavchatProvider, KavchatChat, KavchatLauncher, useKavchat } from '@kavchat/react-native';

export default function App() {
  return (
    <KavchatProvider publicKey="pk_live_..." onScreen={(screen, id) => navigation.navigate(screen, { id })}>
      <Navigation />
      {/* floating button, bottom end corner; opens the chat in a modal */}
      <KavchatLauncher />
    </KavchatProvider>
  );
}

// or as a screen in your own navigator
function SupportScreen() {
  return <KavchatChat />;
}

// or your own button
function HelpButton() {
  const { available, open } = useKavchat();
  if (!available) return null; // bot is off, limited to testers, or over quota
  return <Button title="Help" onPress={open} />;
}

Props de KavchatProvider

PropDescrição
publicKeypk_live_... do painel. Pode ser incluída no app.
apiBasePadrão: https://api.kavchat.com.
localeIdioma da interface (he, en, ar, ru, es, fr, de, pt, it, tr). Padrão: idioma do dispositivo ou, se não houver tradução, idioma do bot. A correspondência com os roteiros é sempre feita no idioma do bot.
userToken, userNameIdentificação do usuário, veja abaixo.
onScreen(screen, targetId?)Chamada ao tocar em um botão do tipo "Tela" no roteiro. Sem ela, esses botões não fazem nada.
screensTelas que o app pode abrir, para a lista suspensa do editor. Veja Lista de telas no editor.
onEvent(event)ready, unavailable, effect, ticket.
storageQualquer objeto { getItem, setItem, removeItem? }, síncrono ou assíncrono. Padrão: AsyncStorage ou, se não estiver instalado, memória.
themePersonalização de cores, veja Cores e RTL.

useKavchat() retorna { status, available, offline, locale, isOpen, open, close, toggle, setUser, startNewConversation, openContactForm, controller }.

Props de KavchatChat: onClose? (exibe um botão de fechar), hideHeader? e style?. Props de KavchatLauncher: style? (posição), onPress? (para abrir sua própria tela no lugar da janela integrada) e attention? (false desativa a mensagem).

Quando uma mensagem ao lado do botão de chat é configurada no painel de administração (um balão de fala ou texto curvo acima do botão), KavchatLauncher a exibe após o intervalo definido, no máximo uma vez por dia. Fechar a mensagem ou abrir o chat a oculta até o dia seguinte. A mensagem não aparece enquanto o chat está aberto. Se a redução de movimento estiver ativada no dispositivo, a mensagem aparece sem animação. Com a opção "Não mostrar em celulares" ativada, a mensagem aparece apenas em tablets (quando o lado mais curto da tela tem 600 pontos ou mais).

Token de identidade

Sem um token, o usuário é um visitante, e o formulário de contato pede um e-mail ou telefone. Para identificar um usuário conectado, assine um JWT de curta duração no seu servidor com a chave secreta (nunca no app, veja Identificação de usuários) e passe-o assim:

TSX
<KavchatProvider publicKey="pk_live_..." userToken={session?.kavchatToken} userName={session?.firstName}>

Alterar userToken (ao entrar ou sair da conta) recarrega a versão, pois o público de teste depende da identidade do usuário. Você também pode chamar useKavchat().setUser({ token, name }). O token é enviado apenas no cabeçalho Authorization: Bearer e não é salvo no dispositivo. A conversa salva fica vinculada a uma impressão digital do token, então outro usuário no mesmo dispositivo inicia uma nova conversa.

Links, telas e formulário de contato

  • Botões do tipo link abrem apenas endereços https: e tel:, por meio de Linking.openURL. Outros tipos de endereço não são abertos.
  • Botões do tipo screen chamam onScreen. O chat permanece onde estava.
  • O botão "Falar com a equipe" abre o formulário de contato com a conversa anexada. Após o envio, o número do chamado é exibido.

Lista de telas no editor

Declare uma única vez as telas que o app pode abrir na prop screens de KavchatProvider. O SDK envia a lista ao Kavchat automaticamente, e o editor de roteiros oferece essas telas em uma lista suspensa com seus nomes, no lugar de um campo de texto livre. O editor também pede um identificador de item quando a tela precisa dele e avisa quando um botão leva a uma tela não declarada. Ninguém precisa digitar a lista de telas manualmente no painel.

TSX
const screens = [
  { id: 'orders', label: 'My orders' },
  { id: 'product', label: 'Product page', needsTarget: true }, // the button also carries a product id
];

<KavchatProvider
  publicKey="pk_live_..."
  screens={screens}
  onScreen={(screen, targetId) => navigation.navigate(screen, targetId ? { id: targetId } : undefined)}
>
  • id é o valor recebido por onScreen: letras latinas, dígitos, _, . e -, com até 40 caracteres. label é o nome que o administrador vê, com até 60 caracteres. Até 100 telas. Entradas inválidas e identificadores duplicados são descartados.
  • needsTarget: o botão também leva um identificador de item (por exemplo, o identificador de um produto), recebido por onScreen como targetId.
  • A lista é enviada em segundo plano após o carregamento da versão, apenas quando houver mudanças ou uma vez por semana. Falhas não são exibidas, e o envio é tentado novamente na próxima inicialização. O usuário não vê nada.
  • As telas registradas aparecem no painel em Bot, Configurações, "Telas do app", com as plataformas que informaram cada tela. Uma tela removida do app pode ser excluída ali.

Cores e RTL

As cores vêm das configurações do bot no painel (primaryColor), e a cor do texto sobre ela é escolhida conforme o contraste WCAG. Você pode substituir qualquer uma delas: primary, background, botBubble, userBubble, text, muted, accent, onPrimary e error:

TSX
<KavchatProvider
  publicKey="pk_live_..."
  theme={{ primary: '#0f766e', background: '#ffffff' }}
>

O pacote não altera a direção do layout do app. Ele segue I18nManager.isRTL (estilos start e end), e cada texto é alinhado conforme seu idioma. Assim, hebraico e árabe são lidos corretamente mesmo em um app com layout da esquerda para a direita. Se o seu app estiver em hebraico ou árabe, configure RTL uma vez, como de costume: I18nManager.forceRTL(true) e reinicie o app.

O selo "Desenvolvido por Kavchat by maxsave" aparece em todos os planos, com um link para kavchat.com. No Premium, você pode ocultá-lo nas configurações do bot.

Chat com atendente

Não é preciso configurar nada para começar. Ao ativar o chat com atendentes no painel de administração, a opção aparece no chat do app exatamente como no widget para sites.

  • A opção: "Falar com atendente" aparece antes de "Falar com a equipe", e só depois que o servidor confirma que há um atendente disponível. A disponibilidade é verificada novamente ao tocar no botão. Fora do horário de atendimento ou quando todos os atendentes estão ocupados, aparece apenas o formulário de contato, com uma breve explicação e o horário de retorno da equipe, se conhecido. Quando a cota mensal acaba, o formulário aparece sem explicação.
  • A solicitação: visitantes deixam um e-mail ou telefone caso nenhum atendente fique disponível a tempo. Também podem adicionar um nome e uma primeira mensagem. Quando há mais de um departamento aberto, escolhem um deles. Usuários identificados (userToken) não precisam preencher dados de contato. Se nenhum atendente puder aceitar a conversa, o formulário de contato é aberto com os dados já preenchidos.
  • A espera: aparece "Aguardando atendente" com uma contagem regressiva até o fim do tempo de espera que você definiu, ao lado dos botões "Deixar mensagem em vez de esperar" e "Encerrar conversa". O usuário já pode escrever enquanto espera. Quando o tempo acaba, a conversa vira um chamado automaticamente. São exibidos o número do chamado e uma explicação de que nenhum atendente ficou disponível a tempo.
  • A conversa: nome do atendente, mensagens, indicador de digitação, transferência para outro atendente e encerramento da conversa. Tudo que vem do servidor é exibido como texto simples, sem links.
  • Em tempo real: conexão Server-Sent Events via XMLHttpRequest (ou fetch com streaming, quando disponível). Se a conexão cair, o chat se reconecta em intervalos cada vez maiores e recupera tudo o que ficou faltando. Assim, nenhuma mensagem se perde ou aparece duas vezes. Em redes que bloqueiam essa conexão, o chat passa a fazer consultas repetidas (long-poll). Cada mensagem tem um identificador, então uma nova tentativa não gera duplicatas. Mensagens que não foram enviadas são sinalizadas e podem ser reenviadas com um toque.
  • Segundo plano e reinicialização: quando o app vai para segundo plano, a conexão é fechada. Quando ele volta, o chat recupera o que ficou faltando. A credencial de acesso à conversa vale por até 12 horas ou até a conversa terminar e fica no armazenamento que você escolher. Assim, após reiniciar o app, uma conversa em andamento volta à tela. A credencial não é salva no endereço nem registrada em logs.
  • Sem notificações push nesta versão: mensagens do atendente chegam apenas enquanto o app está aberto.

Recomendamos guardar a credencial de acesso em um armazenamento seguro (Keychain no iOS, Keystore no Android). Se você não configurar secureStorage, a credencial fica no mesmo armazenamento da conversa. Um botão próprio para falar com atendente abre o formulário de solicitação quando há um atendente disponível. Se não houver, abre o formulário de contato com a explicação:

TSX
import * as SecureStore from 'expo-secure-store';

// expo-secure-store allows only [A-Za-z0-9._-] in keys
const safe = (k: string) => k.replace(/[^A-Za-z0-9._-]/g, '_');
const secureStorage = {
  getItem: (k: string) => SecureStore.getItemAsync(safe(k)),
  setItem: (k: string, v: string) => SecureStore.setItemAsync(safe(k), v),
  removeItem: (k: string) => SecureStore.deleteItemAsync(safe(k)),
};

<KavchatProvider publicKey="pk_live_..." secureStorage={secureStorage} livePage="checkout">

// your own button
const { openLive } = useKavchat();
<Button title="Chat with us" onPress={openLive} />

Eventos: onEvent recebe { type: 'live', phase } a cada mudança de estado da conversa (queued, active, closed, ticket, replaced, expired).

Perguntas, condições e leads

Não é preciso configurar nada no app. Um roteiro pode pedir dados (nome, telefone, e-mail, número ou texto), decidir como continuar com base em condições (Premium) e enviar um lead. Tudo é executado no dispositivo.

  • Teclado adequado: quando o bot espera uma resposta, o campo de texto apresenta um teclado de telefone, e-mail ou números, com preenchimento automático. Se a resposta for inválida, o bot explica o que falta e o campo fica destacado. Perguntas que podem ser puladas exibem o botão "Pular".
  • Lead: quando há um texto de consentimento, aparecem os botões "Enviar" e "Não, obrigado". Se não houver conexão com a rede, o lead é salvo no dispositivo e enviado quando a conexão voltar, sem duplicatas.
  • Preenchimento prévio: as respostas já fornecidas pelo usuário preenchem o formulário de contato e o formulário de solicitação de atendente e são anexadas ao chamado e à conversa com o atendente. Nome, e-mail e telefone do token de identidade são considerados respostas conhecidas, e o bot não pergunta por eles novamente.
  • Tela atual: livePage também indica a tela que as condições verificam e que é enviada com os leads. Alterar a prop atualiza essa informação.

Respostas de IA (Premium)

Não há nada para configurar no app. Ao ativar as respostas de IA no painel de administração (com sua própria chave de um provedor de IA), uma pergunta que nenhum roteiro responde é enviada ao Kavchat. Durante a espera, de até 20 segundos, o chat mostra que o bot está digitando. A resposta aparece como texto simples, com uma pequena etiqueta "Resposta de IA" no idioma da interface. Abaixo dela, aparecem um botão para um assunto relacionado, se houver, e o botão "Falar com a equipe".

Se não houver resposta, ou se a solicitação falhar, demorar demais ou ultrapassar o limite (10 respostas por conversa a cada 15 minutos), o chat exibe a mensagem padrão de "Não encontrei uma resposta". A pergunta continua aparecendo no relatório de perguntas sem resposta, e as respostas de IA são mantidas na conversa salva como qualquer outra mensagem.

Comportamento

  • A versão publicada é armazenada em cache com seu ETag (If-None-Match, 304). Sem conexão com a rede, a versão salva é exibida com um aviso.
  • A conversa é salva no dispositivo e restaurada por 24 horas, desde que a versão e o usuário não tenham mudado.
  • O percurso da conversa é enviado 2 segundos após a última ação e também quando o app vai para segundo plano.
  • As requisições não enviam cabeçalhos personalizados, apenas Authorization, Content-Type e If-None-Match.