BoaVoz
Documentação · Integrações

Chat dentro de apps

O mesmo chat do site abre dentro do seu app, numa webview, já com a pessoa identificada: as ações recebem quem ela é e a conta ativa, como no site. O app só precisa de uma página com o widget em tela cheia e de um jeito de pedir o token ao seu servidor.

Como funciona

  1. Seu servidor assina o token da pessoa com o segredo de identidade, como no site (veja Identificar usuários). O token vale 1 hora e o widget pede outro 5 minutos antes de vencer.
  2. Uma página sua (por exemplo https://seuapp.com/chat-app.html) carrega o widget.js com mode: "fullscreen": o chat já abre, ocupando a tela toda, sem o botão flutuante.
  3. O app abre essa página numa webview e passa a sessão dele antes de a página carregar. O getToken da página usa essa sessão para pedir o token ao seu servidor.
  4. O X do chat chama o onClose da página, que avisa o app para fechar a tela.
O segredo de identidade fica só no seu servidor. Nem a página nem o app guardam o segredo: eles só repassam o token pronto.

A página do chat

html
<!doctype html>
<html lang="pt-BR">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body style="margin:0">
  <script>
    window.ChatWidgetConfig = {
      mode: "fullscreen",                 // já aberto, na tela toda, sem o botão
      // o mesmo token do site, pedido ao SEU servidor com a sessão que o app passou
      getToken: async () => {
        const r = await fetch("https://api.seuapp.com/chat-token", {
          headers: { Authorization: "Bearer " + window.APP_SESSION },
        });
        return r.text();
      },
      // o X do chat: avise o app para fechar a tela
      onClose: () => window.ReactNativeWebView?.postMessage("fechar"),
    };
  </script>
  <script src="https://SEU-DOMINIO/widget.js" data-key="CHAVE_DO_WIDGET" async></script>
</body>
</html>
OpçãoO que faz
mode: "fullscreen"Também como data-mode="fullscreen" no <script>. O chat abre na hora, na tela toda, sem o botão, a prévia e o contador.
getTokenO mesmo do site: chamado ao abrir e 5 minutos antes de o token vencer. Sem ele, a conversa é anônima.
onCloseChamado quando a pessoa toca no X do chat. No modo tela cheia, o chat não some sozinho: quem fecha é o app.

No app

React Native

tsx
// React Native (react-native-webview)
import { WebView } from "react-native-webview";

export function Atendimento({ sessao, fechar }) {
  return (
    <WebView
      source={{ uri: "https://seuapp.com/chat-app.html" }}
      // a sessão do app entra antes da página carregar (getToken usa)
      injectedJavaScriptBeforeContentLoaded={`window.APP_SESSION = ${JSON.stringify(sessao)}; true;`}
      onMessage={(e) => e.nativeEvent.data === "fechar" && fechar()}
      // fotos e áudios: o seletor de arquivos do sistema
      allowFileAccess
      mediaPlaybackRequiresUserAction={false}
    />
  );
}

Flutter

dart
// Flutter (webview_flutter 4)
final controller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..addJavaScriptChannel("App", onMessageReceived: (m) {
    if (m.message == "fechar") Navigator.of(context).pop();
  })
  ..setNavigationDelegate(NavigationDelegate(
    onPageStarted: (_) => controller.runJavaScript(
      "window.APP_SESSION = ${jsonEncode(sessao)};"),
  ))
  ..loadRequest(Uri.parse("https://seuapp.com/chat-app.html"));
// na página, troque o onClose por: () => App.postMessage("fechar")

No Android nativo, o caminho é o mesmo com addJavascriptInterface (a ponte para o onClose) e evaluateJavascript (a sessão); no iOS, com WKUserScript no início do documento e um WKScriptMessageHandler.

Detalhes

  • Sair da conta: chame ChatWidget.logout() antes de fechar a webview, ou limpe os dados dela. A conversa de nível usuário já é presa ao token: outra pessoa no mesmo aparelho nunca vê a conversa da anterior.
  • Trocar de conta (workspace): ChatWidget.setContext(novoToken) abre uma conversa nova, no contexto novo, como no site.
  • Fotos e arquivos: o botão de anexo usa o seletor de arquivos do sistema; libere o acesso a fotos e câmera na webview do seu app.
  • Domínio: o widget.js pode vir do domínio próprio da agência; a página do chat é sua e pode ficar em qualquer lugar.