

# SDK de Mensagens para Clientes do Chat do IVS: Tutorial do React Native, parte 1: salas de chat
<a name="chat-sdk-react-tutorial-chat-rooms"></a>

Esta é a primeira de um tutorial de duas partes. Você aprenderá os fundamentos do trabalho com o SDK de Mensagens do JavaScript para Clientes do Chat do Amazon IVS ao criar uma aplicação totalmente funcional usando o React Native. Chamamos a aplicação de *Chatterbox*.

O público-alvo são desenvolvedores experientes, mas iniciantes no SDK Amazon IVS Chat Messaging. Você deve ficar confortável com as linguagens de programação TypeScript ou JavaScript e a biblioteca React Native.

Para resumir, vamos nos referir ao SDK JavaScript do Amazon IVS Chat Client Messaging como o SDK do Chat JS.

**Observação**: em alguns casos, os exemplos de código para JavaScript e TypeScript são idênticos, então eles são combinados.

Esta primeira parte do tutorial está dividida em várias seções:

1. [Configure um servidor local de autenticação/autorização](#chat-react-rooms-auth-server)

1. [Crie um projeto de Chatterbox](#chat-react-rooms-chatterbox)

1. [Conectar a uma sala de chat](#chat-react-rooms-connect)

1. [Crie um provedor de tokens](#chat-react-rooms-token-provider)

1. [Observe as atualizações de conexão](#chat-react-rooms-connection-state)

1. [Crie um componente do botão Enviar](#chat-react-rooms-send-button)

1. [Crie uma entrada de mensagem](#chat-react-rooms-message-input)

1. [Próximas etapas](#chat-react-rooms-next-steps)

## Pré-requisitos
<a name="chat-react-rooms-prerequisites"></a>
+ Familiarize-se com o TypeScript ou o JavaScript e com a biblioteca React Native. Se você não estiver familiarizado com o React Native, aprenda o básico em [Intro to React Native](https://reactnative.dev/docs/tutorial) (Introdução ao React Native).
+ Leia e compreenda [Conceitos básicos do Amazon IVS Chat](getting-started-chat.md).
+ Crie um usuário do AWS IAM com os recursos createChatToken e createRoom definidos em uma política do IAM existente. (Consulte [Conceitos básicos do Amazon IVS Chat](getting-started-chat.md).)
+ Certifique-se de que as chaves secretas/de acesso desse usuário estejam armazenadas em um arquivo de credenciais da AWS. Para obter instruções, consulte o [Guia do usuário da AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) (especialmente [Configuração e definições do arquivo de credenciais](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)).
+ Crie uma sala de chat e salve seu ARN. Consulte [Conceitos básicos do Amazon IVS Chat](getting-started-chat.md). (Se você não salvar o ARN, poderá consultá-lo posteriormente com o console ou a API do Chat.)
+ Instale o ambiente Node.js 14\$1 com o gerenciador de pacotes NPM ou Yarn.

## Configure um servidor local de autenticação/autorização
<a name="chat-react-rooms-auth-server"></a>

Sua aplicação de backend é responsável por criar salas de chat e gerar os tokens de chat necessários para que o SDK do Chat JS autentique e autorize seus clientes em suas salas de chat. Você deverá usar seu próprio backend, pois não é possível armazenar com segurança as chaves da AWS em uma aplicação móvel. Invasores sofisticados podem extraí-las e obter acesso à sua conta da AWS.

Consulte [Criar um token de chat](getting-started-chat-auth.md) em *Introdução ao Amazon IVS Chat*. Conforme mostrado no fluxograma, sua aplicação do lado do servidor é responsável por criar um token de chat. Isso significa que sua aplicação deve fornecer seu próprio meio de gerar um token de chat solicitando-o da sua aplicação a partir do lado do servidor.

Nesta seção, você aprenderá os fundamentos da criação de um provedor de tokens em seu backend. Usamos a estrutura expressa para criar um servidor local ativo que gerencia a criação de tokens de chat usando seu ambiente local da AWS.

Crie um projeto `npm` vazio usando o NPM. Crie um diretório para manter sua aplicação e torne-o seu diretório de trabalho:

```
$ mkdir backend & cd backend
```

Use `npm init` para criar um arquivo `package.json` para sua aplicação:

```
$ npm init
```

Esse comando solicita várias coisas, incluindo o nome e a versão da sua aplicação. Por enquanto, basta pressionar **RETURN** para aceitar os padrões da maioria deles, com a seguinte exceção:

```
entry point: (index.js)
```

Pressione **RETURN** para aceitar o nome de arquivo padrão sugerido `index.js` ou digite o que você quiser que seja o nome do arquivo principal.

Agora instale as dependências necessárias:

```
$ npm install express aws-sdk cors dotenv
```

`aws-sdk` requer variáveis de ambiente de configuração que são carregadas automaticamente de um arquivo chamado `.env` localizado no diretório raiz. Para configurá-lo, crie um novo arquivo chamado `.env` e preencha as informações de configuração ausentes:

```
# .env

# The region to send service requests to.
AWS_REGION=us-west-2

# Access keys use an access key ID and secret access key
# that you use to sign programmatic requests to AWS.

# AWS access key ID.
AWS_ACCESS_KEY_ID=...

# AWS secret access key.
AWS_SECRET_ACCESS_KEY=...
```

Agora, criamos um arquivo de ponto de entrada no diretório raiz com o nome que você inseriu acima no comando `npm init`. Nesse caso, usamos `index.js` e importamos todos os pacotes necessários:

```
// index.js
import express from 'express';
import AWS from 'aws-sdk';
import 'dotenv/config';
import cors from 'cors';
```

Agora, crie uma nova instância de `express`:

```
const app = express();
const port = 3000;

app.use(express.json());
app.use(cors({ origin: ['http://127.0.0.1:5173'] }));
```

Depois disso, será possível criar seu primeiro método POST de endpoint para o provedor do token. Pegue os parâmetros necessários no corpo da solicitação (`roomId`, `userId`, `capabilities` e `sessionDurationInMinutes`):

```
app.post('/create_chat_token', (req, res) => {
  const { roomIdentifier, userId, capabilities, sessionDurationInMinutes } = req.body || {};
});
```

Adicione a validação dos campos obrigatórios:

```
app.post('/create_chat_token', (req, res) => {
  const { roomIdentifier, userId, capabilities, sessionDurationInMinutes } = req.body || {};

  if (!roomIdentifier || !userId) {
    res.status(400).json({ error: 'Missing parameters: `roomIdentifier`, `userId`' });
    return;
  }
});
```

Depois de preparar o método POST, integramos `createChatToken` com `aws-sdk` para a funcionalidade principal de autenticação/autorização:

```
app.post('/create_chat_token', (req, res) => {
  const { roomIdentifier, userId, capabilities, sessionDurationInMinutes } = req.body || {};

  if (!roomIdentifier || !userId || !capabilities) {
    res.status(400).json({ error: 'Missing parameters: `roomIdentifier`, `userId`, `capabilities`' });
    return;
  }

  ivsChat.createChatToken({ roomIdentifier, userId, capabilities, sessionDurationInMinutes }, (error, data) => {
    if (error) {
      console.log(error);
      res.status(500).send(error.code);
    } else if (data.token) {
      const { token, sessionExpirationTime, tokenExpirationTime } = data;
      console.log(`Retrieved Chat Token: ${JSON.stringify(data, null, 2)}`);

      res.json({ token, sessionExpirationTime, tokenExpirationTime });
    }
  });
});
```

No final do arquivo, adicione um receptor de portas para sua aplicação `express`:

```
app.listen(port, () => {
  console.log(`Backend listening on port ${port}`);
});
```

Agora é possível executar o servidor com a linha de comando a seguir na raiz do projeto:

```
$ node index.js
```

**Dica**: este servidor aceita solicitações de URL em https://localhost:3000.

## Crie um projeto de Chatterbox
<a name="chat-react-rooms-chatterbox"></a>

Primeiro, você cria o projeto do React denominado `chatterbox`. Execute este comando: 

```
npx create-expo-app
```

Ou crie um projeto de exposição com um modelo do TypeScript.

```
npx create-expo-app -t expo-template-blank-typescript
```

É possível integrar o SDK do Chat Client Messaging JS por meio do [Gerenciador de pacotes de nó](https://www.npmjs.com/) ou do [Gerenciador de pacotes Yarn](https://yarnpkg.com/):
+ Npm: `npm install amazon-ivs-chat-messaging`
+ Yarn: `yarn add amazon-ivs-chat-messaging`

## Conectar a uma sala de chat
<a name="chat-react-rooms-connect"></a>

Aqui você cria uma `ChatRoom` e se conecta a ela usando métodos assíncronos. A classe `ChatRoom` gerencia a conexão do usuário com o SDK do Chat JS. Para se conectar com sucesso a uma sala de chat, você deve fornecer uma instância de `ChatToken` dentro da sua aplicação React. 

Navegue até o arquivo `App` criado no projeto `chatterbox` padrão e exclua tudo que for retornado por um componente funcional. Nenhum código pré-preenchido é necessário. Neste ponto, a nossa `App` está bem vazia.

**TypeScript/JavaScript**:

```
// App.tsx / App.jsx

import * as React from 'react';
import { Text } from 'react-native';

export default function App() {
  return <Text>Hello!</Text>;
}
```

Crie uma nova instância de `ChatRoom` e passe-a para o estado usando o hook `useState`. Ela exige a passagem de `regionOrUrl` (a região da AWS na qual sua sala de chat está hospedada) e `tokenProvider` (usado para o fluxo de autenticação/autorização de backend criado nas etapas subsequentes).

**Importante**: você deve usar a mesma região da AWS em que criou a sala em [Conceitos básicos do Amazon IVS Chat](getting-started-chat-create-room.md). A API é um serviço regional da AWS. Para obter uma lista das regiões com suporte e dos endpoints do serviço HTTPS do Amazon IVS Chat, consulte a página de [Regiões do Amazon IVS Chat](https://docs.aws.amazon.com/general/latest/gr/ivs.html#ivs_region).

**TypeScript/JavaScript**:

```
// App.jsx / App.tsx

import React, { useState } from 'react';
import { Text } from 'react-native';
import { ChatRoom } from 'amazon-ivs-chat-messaging';

export default function App() {
  const [room] = useState(() =>
    new ChatRoom({
      regionOrUrl: process.env.REGION,
      tokenProvider: () => {},
    }),
  );

  return <Text>Hello!</Text>;
}
```

## Crie um provedor de tokens
<a name="chat-react-rooms-token-provider"></a>

Como próxima etapa, precisamos criar uma função `tokenProvider` sem parâmetros que seja exigida pelo construtor `ChatRoom`. Primeiro, criaremos uma função `fetchChatToken` que fará uma solicitação POST para a aplicação de backend que você configurou em [Configure um servidor local de autenticação/autorização](#chat-react-rooms-auth-server). Os tokens de chat contêm as informações necessárias para que o SDK estabeleça uma conexão com a sala de chat com êxito. A API do Chat usa esses tokens como uma forma segura de validar a identidade, os recursos de um usuário em uma sala de chat e a duração da sessão.

No navegador do projeto, crie um novo arquivo TypeScript/JavaScript chamado `fetchChatToken`. Crie uma solicitação de busca para a aplicação `backend` e retorne o objeto `ChatToken` da resposta. Adicione as propriedades do corpo da solicitação necessárias para a criação de um token de chat. Use as regras definidas para [nomes do recurso da Amazon (ARNs)](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html). Essas propriedades estão documentadas na operação [CreateChatToken](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_CreateChatToken.html#API_CreateChatToken_RequestBody).

**Observação**: o URL que você está usando aqui é o mesmo URL que seu servidor local criou quando você executou a aplicação de backend.

------
#### [ TypeScript ]

```
// fetchChatToken.ts

import { ChatToken } from 'amazon-ivs-chat-messaging';

type UserCapability = 'DELETE_MESSAGE' | 'DISCONNECT_USER' | 'SEND_MESSAGE';

export async function fetchChatToken(
  userId: string,
  capabilities: UserCapability[] = [],
  attributes?: Record<string, string>,
  sessionDurationInMinutes?: number,
): Promise<ChatToken> {
  const response = await fetch(`${process.env.BACKEND_BASE_URL}/create_chat_token`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      userId,
      roomIdentifier: process.env.ROOM_ID,
      capabilities,
      sessionDurationInMinutes,
      attributes
    }),
  });

  const token = await response.json();

  return {
    ...token,
    sessionExpirationTime: new Date(token.sessionExpirationTime),
    tokenExpirationTime: new Date(token.tokenExpirationTime),
  };
}
```

------
#### [ JavaScript ]

```
// fetchChatToken.js

export async function fetchChatToken(
  userId,
  capabilities = [],
  attributes,
  sessionDurationInMinutes) {
  const response = await fetch(`${process.env.BACKEND_BASE_URL}/create_chat_token`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      userId,
      roomIdentifier: process.env.ROOM_ID,
      capabilities,
      sessionDurationInMinutes,
      attributes
    }),
  });

  const token = await response.json();

  return {
    ...token,
    sessionExpirationTime: new Date(token.sessionExpirationTime),
    tokenExpirationTime: new Date(token.tokenExpirationTime),
  };
}
```

------

## Observe as atualizações de conexão
<a name="chat-react-rooms-connection-state"></a>

Reagir às mudanças no estado da conexão de uma sala de chat é parte essencial da criação de uma aplicação de chat. Vamos começar assinando eventos relevantes:

**TypeScript/JavaScript**:

```
// App.tsx / App.jsx

import React, { useState, useEffect } from 'react';
import { Text } from 'react-native';
import { ChatRoom } from 'amazon-ivs-chat-messaging';
import { fetchChatToken } from './fetchChatToken';

export default function App() {
  const [room] = useState(
    () =>
      new ChatRoom({
        regionOrUrl: process.env.REGION,
        tokenProvider: () => fetchChatToken('Mike', ['SEND_MESSAGE']),
      }),
  );

  useEffect(() => {
    const unsubscribeOnConnecting = room.addListener('connecting', () => {});
    const unsubscribeOnConnected = room.addListener('connect', () => {});
    const unsubscribeOnDisconnected = room.addListener('disconnect', () => {});

    return () => {
      // Clean up subscriptions.
      unsubscribeOnConnecting();
      unsubscribeOnConnected();
      unsubscribeOnDisconnected();
    };
  }, [room]);

  return <Text>Hello!</Text>;
}
```

Em seguida, precisamos fornecer a capacidade de ler o estado da conexão. Usamos nosso hook `useState` para criar algum estado local em `App` e definir o estado da conexão dentro de cada receptor.

**TypeScript/JavaScript**:

```
// App.tsx / App.jsx

import React, { useState, useEffect } from 'react';
import { Text } from 'react-native';
import { ChatRoom, ConnectionState } from 'amazon-ivs-chat-messaging';
import { fetchChatToken } from './fetchChatToken';

export default function App() {  
  const [room] = useState(
    () =>
      new ChatRoom({
        regionOrUrl: process.env.REGION,
        tokenProvider: () => fetchChatToken('Mike', ['SEND_MESSAGE']),
      }),
  );
  const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');

  useEffect(() => {
    const unsubscribeOnConnecting = room.addListener('connecting', () => {
      setConnectionState('connecting');
    });

    const unsubscribeOnConnected = room.addListener('connect', () => {
      setConnectionState('connected');
    });

    const unsubscribeOnDisconnected = room.addListener('disconnect', () => {
      setConnectionState('disconnected');
    });

    return () => {
      unsubscribeOnConnecting();
      unsubscribeOnConnected();
      unsubscribeOnDisconnected();
    };
  }, [room]);

  return <Text>Hello!</Text>;
}
```

Depois de se inscrever no estado da conexão, exiba o estado da conexão e conecte-se à sala de chat usando o método `room.connect` dentro do hook `useEffect`:

**TypeScript/JavaScript**:

```
// App.tsx / App.jsx

// ...

useEffect(() => {
  const unsubscribeOnConnecting = room.addListener('connecting', () => {
    setConnectionState('connecting');
  });

  const unsubscribeOnConnected = room.addListener('connect', () => {
    setConnectionState('connected');
  });

  const unsubscribeOnDisconnected = room.addListener('disconnect', () => {
    setConnectionState('disconnected');
  });

  room.connect();

  return () => {
    unsubscribeOnConnecting();
    unsubscribeOnConnected();
    unsubscribeOnDisconnected();
  };
}, [room]);

// ...

return (
  <SafeAreaView style={styles.root}>
    <Text>Connection State: {connectionState}</Text>
  </SafeAreaView>
);

const styles = StyleSheet.create({
  root: {
    flex: 1,
  }
});

// ...
```

Você implementou com êxito uma conexão de sala de chat.

## Crie um componente do botão Enviar
<a name="chat-react-rooms-send-button"></a>

Nesta seção, você criará um botão de envio que tem um design diferente para cada estado de conexão. O botão de envio facilita enviar mensagens em uma sala de chat. Ele também serve como um indicador visual de se/quando as mensagens podem ser enviadas; por exemplo, em caso de conexões perdidas ou sessões de chat expiradas.

Primeiro, crie um novo arquivo no diretório `src` do seu projeto do Chatterbox e dê a ele o nome `SendButton`. Em seguida, crie um componente que exibirá um botão para sua aplicação de chat. Exporte seu `SendButton` e importe-o para `App`. No `<View></View>` vazio, adicione `<SendButton />`.

------
#### [ TypeScript ]

```
// SendButton.tsx

import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator, StyleSheet } from 'react-native';

interface Props {
  onPress?: () => void;
  disabled: boolean;
  loading: boolean;
}

export const SendButton = ({ onPress, disabled, loading }: Props) => {
  return (
    <TouchableOpacity style={styles.root} disabled={disabled} onPress={onPress}>
      {loading ? <Text>Send</Text> : <ActivityIndicator />}
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  root: {
    width: 50,
    height: 50,
    borderRadius: 30,
    marginLeft: 10,
    justifyContent: 'center',
    alignContent: 'center',
  }
});

// App.tsx

import { SendButton } from './SendButton';

// ...

return (
  <SafeAreaView style={styles.root}>
    <Text>Connection State: {connectionState}</Text>
    <SendButton />
  </SafeAreaView>
);
```

------
#### [ JavaScript ]

```
// SendButton.jsx

import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator, StyleSheet } from 'react-native';

export const SendButton = ({ onPress, disabled, loading }) => {
  return (
    <TouchableOpacity style={styles.root} disabled={disabled} onPress={onPress}>
      {loading ? <Text>Send</Text> : <ActivityIndicator />}
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  root: {
    width: 50,
    height: 50,
    borderRadius: 30,
    marginLeft: 10,
    justifyContent: 'center',
    alignContent: 'center',
  }
});

// App.jsx

import { SendButton } from './SendButton';

// ...

return (
  <SafeAreaView style={styles.root}>
    <Text>Connection State: {connectionState}</Text>
    <SendButton />
  </SafeAreaView>
);
```

------

Em seguida, em `App`, defina uma função chamada `onMessageSend` e passe-a para a propriedade `SendButton onPress`. Defina outra variável chamada `isSendDisabled` (que impede o envio de mensagens quando a sala não estiver conectada) e passe-a para a propriedade `SendButton disabled`.

**TypeScript/JavaScript**:

```
// App.jsx / App.tsx

// ...

const onMessageSend = () => {};

const isSendDisabled = connectionState !== 'connected';

return (
  <SafeAreaView style={styles.root}>
    <Text>Connection State: {connectionState}</Text>
    <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
  </SafeAreaView>
);

// ...
```

## Crie uma entrada de mensagem
<a name="chat-react-rooms-message-input"></a>

A barra de mensagens do Chatterbox é o componente com o qual você interagirá para enviar mensagens para uma sala de chat. Normalmente, ela contém uma entrada de texto para redigir sua mensagem e um botão para enviar sua mensagem.

Para criar um componente `MessageInput`, primeiro crie um novo arquivo no diretório `src` e dê a ele o nome `MessageInput`. Em seguida, crie um componente de entrada que exibirá uma entrada para sua aplicação de chat. Exporte sua `MessageInput` e importe-a para `App` (acima do `<SendButton />`).

Crie um novo estado chamado `messageToSend` usando o hook `useState`, com uma string vazia como valor padrão. No corpo da sua aplicação, passe `messageToSend` para o `value` de `MessageInput` e passe `setMessageToSend` para a propriedade `onMessageChange`:

------
#### [ TypeScript ]

```
// MessageInput.tsx

import * as React from 'react';

interface Props {
  value?: string;
  onValueChange?: (value: string) => void;
}

export const MessageInput = ({ value, onValueChange }: Props) => {
  return (
    <TextInput style={styles.input} value={value} onChangeText={onValueChange} placeholder="Send a message" />
  );
};

const styles = StyleSheet.create({
  input: {
    fontSize: 20,
    backgroundColor: 'rgb(239,239,240)',
    paddingHorizontal: 18,
    paddingVertical: 15,
    borderRadius: 50,
    flex: 1,
  }
})

// App.tsx

// ...

import { MessageInput } from './MessageInput';

// ...

export default function App() {
  const [messageToSend, setMessageToSend] = useState('');

// ...

return (
  <SafeAreaView style={styles.root}>
    <Text>Connection State: {connectionState}</Text>
    <View style={styles.messageBar}>
      <MessageInput value={messageToSend} onMessageChange={setMessageToSend} />
      <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
    </View>
  </SafeAreaView>
);

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  messageBar: {
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: 'rgb(160,160,160)',
    flexDirection: 'row',
    padding: 16,
    alignItems: 'center',
    backgroundColor: 'white',
  }
});
```

------
#### [ JavaScript ]

```
// MessageInput.jsx

import * as React from 'react';

export const MessageInput = ({ value, onValueChange }) => {
  return (
    <TextInput style={styles.input} value={value} onChangeText={onValueChange} placeholder="Send a message" />
  );
};

const styles = StyleSheet.create({
  input: {
    fontSize: 20,
    backgroundColor: 'rgb(239,239,240)',
    paddingHorizontal: 18,
    paddingVertical: 15,
    borderRadius: 50,
    flex: 1,
  }
})

// App.jsx

// ...

import { MessageInput } from './MessageInput';

// ...

export default function App() {
  const [messageToSend, setMessageToSend] = useState('');

// ...

return (
  <SafeAreaView style={styles.root}>
    <Text>Connection State: {connectionState}</Text>
    <View style={styles.messageBar}>
      <MessageInput value={messageToSend} onMessageChange={setMessageToSend} />
      <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
    </View>
  </SafeAreaView>
);

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  messageBar: {
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: 'rgb(160,160,160)',
    flexDirection: 'row',
    padding: 16,
    alignItems: 'center',
    backgroundColor: 'white',
  }
});
```

------

## Próximas etapas
<a name="chat-react-rooms-next-steps"></a>

Agora que você terminou de criar uma barra de mensagens para o Chatterbox, vá para a parte 2 deste tutorial do React Native, [Mensagens e eventos](chat-sdk-react-tutorial-messages-events.md).