SDK de Mensagens para Clientes do Chat do IVS: Tutorial do React Native, parte 2: mensagens e eventos - Amazon IVS

SDK de Mensagens para Clientes do Chat do IVS: Tutorial do React Native, parte 2: mensagens e eventos

Esta segunda e última parte do tutorial é dividida em várias seções:

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

Pré-requisito

Certifique-se de ter concluído a parte 1 deste tutorial, salas de chat.

Inscreva-se em eventos de mensagens de chat

A instância ChatRoom usa eventos para se comunicar, quando os eventos ocorrem em uma sala de chat. Para começar a implementar a experiência de chat, você precisa mostrar aos usuários quando as outras pessoas enviam uma mensagem na sala à qual estão conectados.

Aqui, você se inscreve em eventos de mensagens de chat Posteriormente, mostraremos como atualizar uma lista de mensagens que você criou e é atualizada com cada mensagem/evento.

Em seu App, dentro do hook useEffect, inscreva-se em todos os eventos de mensagens:

TypeScript/JavaScript:

// App.tsx / App.jsx useEffect(() => { // ... const unsubscribeOnMessageReceived = room.addListener('message', (message) => {}); return () => { // ... unsubscribeOnMessageReceived(); }; }, []);

Exibir mensagens recebidas

Receber mensagens é parte essencial da experiência de chat. Usando o SDK do Chat JS, é possível configurar seu código para receber facilmente eventos de outros usuários conectados a uma sala de chat.

Posteriormente, mostraremos como realizar ações em uma sala de chat que utilizam os componentes criados por você aqui.

Em sua App, defina um estado chamado messages, com um tipo de matriz ChatMessage chamado messages:

TypeScript
// App.tsx // ... import { ChatRoom, ChatMessage, ConnectionState } from 'amazon-ivs-chat-messaging'; export default function App() { const [messages, setMessages] = useState<ChatMessage[]>([]); //... }
JavaScript
// App.jsx // ... import { ChatRoom, ConnectionState } from 'amazon-ivs-chat-messaging'; export default function App() { const [messages, setMessages] = useState([]); //... }

Em seguida, na função de receptor da message, acrescente message à matriz messages:

TypeScript/JavaScript:

// App.tsx / App.jsx // ... const unsubscribeOnMessageReceived = room.addListener('message', (message) => { setMessages((msgs) => [...msgs, message]); }); // ...

Abaixo, analisamos as tarefas para mostrar as mensagens recebidas:

Criação de um componente de mensagem

O componente Message é responsável por renderizar o conteúdo de uma mensagem recebida pela sua sala de chat. Nesta seção, você cria um componente de mensagens para renderizar mensagens de chat individuais na App.

Crie um novo arquivo no diretório src e atribua a ele o nome Message. Passe o tipo ChatMessage para esse componente e, em seguida, passe a string content das propriedades de ChatMessage para exibir o texto da mensagem recebida dos receptores de mensagens da sala de chat. No Project Navigator, acesse Message.

TypeScript
// Message.tsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { ChatMessage } from 'amazon-ivs-chat-messaging'; type Props = { message: ChatMessage; } export const Message = ({ message }: Props) => { return ( <View style={styles.root}> <Text>{message.sender.userId}</Text> <Text style={styles.textContent}>{message.content}</Text> </View> ); }; const styles = StyleSheet.create({ root: { backgroundColor: 'silver', padding: 6, borderRadius: 10, marginHorizontal: 12, marginVertical: 5, marginRight: 50, }, textContent: { fontSize: 17, fontWeight: '500', flexShrink: 1, }, });
JavaScript
// Message.jsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; export const Message = ({ message }) => { return ( <View style={styles.root}> <Text>{message.sender.userId}</Text> <Text style={styles.textContent}>{message.content}</Text> </View> ); }; const styles = StyleSheet.create({ root: { backgroundColor: 'silver', padding: 6, borderRadius: 10, marginHorizontal: 12, marginVertical: 5, marginRight: 50, }, textContent: { fontSize: 17, fontWeight: '500', flexShrink: 1, }, });

Dica: use este componente para armazenar propriedades diferentes que você deseja renderizar em suas linhas de mensagens; por exemplo, URLs de avatar, nomes de usuário e carimbos de data e hora de quando a mensagem foi enviada.

Reconhecimento das mensagens enviadas pelo usuário atual

Para reconhecer a mensagem enviada pelo usuário atual, modificamos o código e criamos um contexto do React para armazenar o userId do usuário atual.

Crie um novo arquivo no diretório src e atribua a ele o nome UserContext:

TypeScript
// UserContext.tsx import React from 'react'; const UserContext = React.createContext<string | undefined>(undefined); export const useUserContext = () => { const context = React.useContext(UserContext); if (context === undefined) { throw new Error('useUserContext must be within UserProvider'); } return context; }; export const UserProvider = UserContext.Provider;
JavaScript
// UserContext.jsx import React from 'react'; const UserContext = React.createContext(undefined); export const useUserContext = () => { const context = React.useContext(UserContext); if (context === undefined) { throw new Error('useUserContext must be within UserProvider'); } return context; }; export const UserProvider = UserContext.Provider;

Observação: aqui usamos o hook useState para armazenar o valor userId. No futuro, será possível usar setUserId para alterar o contexto do usuário ou para fins de login.

Em seguida, substitua userId no primeiro parâmetro passado para tokenProvider usando o contexto criado anteriormente. Adicione o recurso SEND_MESSAGE ao seu provedor de token, conforme especificado abaixo; é necessário enviar mensagens:

TypeScript
// App.tsx // ... import { useUserContext } from './UserContext'; // ... export default function App() { const [messages, setMessages] = useState<ChatMessage[]>([]); const userId = useUserContext(); const [room] = useState( () => new ChatRoom({ regionOrUrl: process.env.REGION, tokenProvider: () => tokenProvider(userId, ['SEND_MESSAGE']), }), ); // ... }
JavaScript
// App.jsx // ... import { useUserContext } from './UserContext'; // ... export default function App() { const [messages, setMessages] = useState([]); const userId = useUserContext(); const [room] = useState( () => new ChatRoom({ regionOrUrl: process.env.REGION, tokenProvider: () => tokenProvider(userId, ['SEND_MESSAGE']), }), ); // ... }

Em seu componente Message, use o UserContext criado antes, declare a variável isMine, corresponda o userId do remetente com o userId do contexto e aplique estilos diferentes de mensagens para o usuário atual.

TypeScript
// Message.tsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { ChatMessage } from 'amazon-ivs-chat-messaging'; import { useUserContext } from './UserContext'; type Props = { message: ChatMessage; } export const Message = ({ message }: Props) => { const userId = useUserContext(); const isMine = message.sender.userId === userId; return ( <View style={[styles.root, isMine && styles.mine]}> {!isMine && <Text>{message.sender.userId}</Text>} <Text style={styles.textContent}>{message.content}</Text> </View> ); }; const styles = StyleSheet.create({ root: { backgroundColor: 'silver', padding: 6, borderRadius: 10, marginHorizontal: 12, marginVertical: 5, marginRight: 50, }, textContent: { fontSize: 17, fontWeight: '500', flexShrink: 1, }, mine: { flexDirection: 'row-reverse', backgroundColor: 'lightblue', }, });
JavaScript
// Message.jsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { ChatMessage } from 'amazon-ivs-chat-messaging'; import { useUserContext } from './UserContext'; export const Message = ({ message }) => { const userId = useUserContext(); const isMine = message.sender.userId === userId; return ( <View style={[styles.root, isMine && styles.mine]}> {!isMine && <Text>{message.sender.userId}</Text>} <Text style={styles.textContent}>{message.content}</Text> </View> ); }; const styles = StyleSheet.create({ root: { backgroundColor: 'silver', padding: 6, borderRadius: 10, marginHorizontal: 12, marginVertical: 5, marginRight: 50, }, textContent: { fontSize: 17, fontWeight: '500', flexShrink: 1, }, mine: { flexDirection: 'row-reverse', backgroundColor: 'lightblue', }, });

Renderização de uma lista de mensagens de chat

Agora, liste as mensagens usando um componente FlatList e Message:

TypeScript
// App.tsx // ... const renderItem = useCallback<ListRenderItem<ChatMessage>>(({ item }) => { return ( <Message key={item.id} message={item} /> ); }, []); return ( <SafeAreaView style={styles.root}> <Text>Connection State: {connectionState}</Text> <FlatList inverted data={messages} renderItem={renderItem} /> <View style={styles.messageBar}> <MessageInput value={messageToSend} onMessageChange={setMessageToSend} /> <SendButton disabled={isSendDisabled} onPress={onMessageSend} /> </View> </SafeAreaView> ); // ...
JavaScript
// App.jsx // ... const renderItem = useCallback(({ item }) => { return ( <Message key={item.id} message={item} /> ); }, []); return ( <SafeAreaView style={styles.root}> <Text>Connection State: {connectionState}</Text> <FlatList inverted data={messages} renderItem={renderItem} /> <View style={styles.messageBar}> <MessageInput value={messageToSend} onMessageChange={setMessageToSend} /> <SendButton disabled={isSendDisabled} onPress={onMessageSend} /> </View> </SafeAreaView> ); // ...

Todas as peças do quebra-cabeça estão prontas para que sua App comece a renderizar as mensagens recebidas pela sua sala de chat. Continue abaixo para ver como realizar ações em uma sala de chat que aproveitem os componentes que você criou.

Executar ações em uma sala de chat

Enviar mensagens e realizar ações de moderador são algumas das principais formas de interagir com uma sala de chat. Aqui, você aprenderá como usar vários objetos de solicitação de chat para realizar ações comuns no Chatterbox, como enviar uma mensagem, excluir uma mensagem e desconectar outros usuários.

Todas as ações em uma sala de chat seguem um padrão comum: para cada ação executada em uma sala de chat, há um objeto de solicitação correspondente. Para cada solicitação, há um objeto de resposta correspondente que você recebe na confirmação da solicitação.

Uma vez que seus usuários recebam as permissões corretas quando você criar um token de chat, eles poderão realizar com êxito as ações correspondentes usando os objetos de solicitação para ver quais solicitações são possíveis de serem realizadas em uma sala de chat.

Abaixo, explicamos como enviar uma mensagem e excluir uma mensagem.

Enviar uma mensagem

A classe SendMessageRequest permite o envio de mensagens em uma sala de chat. Aqui, você modifica sua App para enviar uma solicitação de mensagem usando o componente que criou em Criar uma entrada de mensagem (na parte 1 deste tutorial).

Para começar, defina uma nova propriedade booleana chamada de isSending com o hook useState. Use essa nova propriedade para alternar o estado desabilitado do seu elemento button usando a constante isSendDisabled. No manipulador de eventos do seu SendButton, limpe o valor de messageToSend e defina isSending como verdadeiro.

Como você fará uma chamada de API a partir desse botão, adicionar o booleano isSending ajuda a evitar que várias chamadas de API ocorram ao mesmo tempo, desativando as interações do usuário no seu SendButton até que a solicitação seja concluída.

Observação: o envio de mensagens só funcionará se você adicionar o recurso SEND_MESSAGE ao seu provedor de token, conforme descrito acima em Reconhecer mensagens enviadas pelo usuário atual.

TypeScript/JavaScript:

// App.tsx / App.jsx // ... const [isSending, setIsSending] = useState(false); // ... const onMessageSend = () => { setIsSending(true); setMessageToSend(''); }; // ... const isSendDisabled = connectionState !== 'connected' || isSending; // ...

Prepare a solicitação criando uma nova instância SendMessageRequest, passando o conteúdo da mensagem para o construtor. Depois de definir os estados isSending e messageToSend, chame o método sendMessage, que envia a solicitação para a sala de chat. Por fim, limpe o sinalizador isSending ao receber a confirmação ou rejeição da solicitação.

TypeScript/JavaScript:

// App.tsx / App.jsx // ... import { ChatRoom, ConnectionState, SendMessageRequest } from 'amazon-ivs-chat-messaging' // ... const onMessageSend = async () => { const request = new SendMessageRequest(messageToSend); setIsSending(true); setMessageToSend(''); try { const response = await room.sendMessage(request); } catch (e) { console.log(e); // handle the chat error here... } finally { setIsSending(false); } }; // ...

Experimente o Chatterbox: tente enviar uma mensagem redigindo uma com a sua MessageBar e tocando no seu SendButton. Você deve ver sua mensagem enviada renderizada dentro da MessageList que você criou anteriormente.

Excluir mensagem

Para excluir uma mensagem de uma sala de chat, você precisa ter a capacidade adequada. As capacidades são concedidas durante a inicialização do token de chat que você usa ao se autenticar em uma sala de chat. Para os propósitos desta seção, a ServerApp de Configure um servidor local de autenticação/autorização (na parte 1 deste tutorial) permite que você especifique as capacidades de moderador. Isso é feito em sua aplicação usando o objeto tokenProvider que você criou em Crie um provedor de tokens (também na parte 1).

Aqui você modifica sua Message adicionando uma função para excluir a mensagem.

Primeiro, abra App.tsx e adicione a capacidade DELETE_MESSAGE. (capabilities é o segundo parâmetro da sua função tokenProvider.)

Observação: é assim que sua ServerApp informa às APIs do IVS Chat que o usuário associado ao token de chat resultante pode excluir mensagens em uma sala de chat. Em uma situação real, você provavelmente terá uma lógica de backend mais complexa para gerenciar os recursos do usuário na infraestrutura da sua aplicação de servidor.

TypeScript/JavaScript:

// App.tsx / App.jsx // ... const [room] = useState(() => new ChatRoom({ regionOrUrl: process.env.REGION, tokenProvider: () => tokenProvider(userId, ['SEND_MESSAGE', 'DELETE_MESSAGE']), }), ); // ...

Nas próximas etapas, você atualizará sua Message para exibir um botão de exclusão.

Defina uma nova função chamada onDelete que aceite uma string como um de seus parâmetros e retorne Promise. Para o parâmetro de string, passe o ID da mensagem do componente.

TypeScript
// Message.tsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { ChatMessage } from 'amazon-ivs-chat-messaging'; import { useUserContext } from './UserContext'; export type Props = { message: ChatMessage; onDelete(id: string): Promise<void>; }; export const Message = ({ message, onDelete }: Props) => { const userId = useUserContext(); const isMine = message.sender.userId === userId; const handleDelete = () => onDelete(message.id); return ( <View style={[styles.root, isMine && styles.mine]}> {!isMine && <Text>{message.sender.userId}</Text>} <View style={styles.content}> <Text style={styles.textContent}>{message.content}</Text> <TouchableOpacity onPress={handleDelete}> <Text>Delete<Text/> </TouchableOpacity> </View> </View> ); }; const styles = StyleSheet.create({ root: { backgroundColor: 'silver', padding: 6, borderRadius: 10, marginHorizontal: 12, marginVertical: 5, marginRight: 50, }, content: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, textContent: { fontSize: 17, fontWeight: '500', flexShrink: 1, }, mine: { flexDirection: 'row-reverse', backgroundColor: 'lightblue', }, });
JavaScript
// Message.jsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { ChatMessage } from 'amazon-ivs-chat-messaging'; import { useUserContext } from './UserContext'; export const Message = ({ message, onDelete }) => { const userId = useUserContext(); const isMine = message.sender.userId === userId; const handleDelete = () => onDelete(message.id); return ( <View style={[styles.root, isMine && styles.mine]}> {!isMine && <Text>{message.sender.userId}</Text>} <View style={styles.content}> <Text style={styles.textContent}>{message.content}</Text> <TouchableOpacity onPress={handleDelete}> <Text>Delete<Text/> </TouchableOpacity> </View> </View> ); }; const styles = StyleSheet.create({ root: { backgroundColor: 'silver', padding: 6, borderRadius: 10, marginHorizontal: 12, marginVertical: 5, marginRight: 50, }, content: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, textContent: { fontSize: 17, fontWeight: '500', flexShrink: 1, }, mine: { flexDirection: 'row-reverse', backgroundColor: 'lightblue', }, });

Em seguida, atualize sua renderItem para refletir as alterações mais recentes em seu componente FlatList.

Em App, defina uma função chamada handleDeleteMessage e passe-a para a propriedade MessageList onDelete.

TypeScript
// App.tsx // ... const handleDeleteMessage = async (id: string) => {}; const renderItem = useCallback<ListRenderItem<ChatMessage>>(({ item }) => { return ( <Message key={item.id} message={item} onDelete={handleDeleteMessage} /> ); }, [handleDeleteMessage]); // ...
JavaScript
// App.jsx // ... const handleDeleteMessage = async (id) => {}; const renderItem = useCallback(({ item }) => { return ( <Message key={item.id} message={item} onDelete={handleDeleteMessage} /> ); }, [handleDeleteMessage]); // ...

Prepare uma solicitação criando uma nova instância de DeleteMessageRequest, passando o ID da mensagem relevante para o parâmetro do construtor e chame deleteMessage, que aceita a solicitação preparada acima:

TypeScript
// App.tsx // ... const handleDeleteMessage = async (id: string) => { const request = new DeleteMessageRequest(id); await room.deleteMessage(request); }; // ...
JavaScript
// App.jsx // ... const handleDeleteMessage = async (id) => { const request = new DeleteMessageRequest(id); await room.deleteMessage(request); }; // ...

Em seguida, atualize o estado de messages para refletir uma nova lista de mensagens que omita a mensagem que você acabou de excluir.

No hook useEffect, receba o evento messageDelete e atualize sua matriz de estados de messages excluindo a mensagem com um ID correspondente ao parâmetro message.

Observação: o evento messageDelete pode ser gerado para que as mensagens sejam excluídas pelo usuário atual ou por qualquer outro usuário na sala. Manipulá-lo no manipulador de eventos (em vez de junto à solicitação deleteMessage) permite unificar o tratamento da exclusão de mensagens.

TypeScript/JavaScript:

// App.tsx / App.jsx // ... const unsubscribeOnMessageDeleted = room.addListener('messageDelete', (deleteMessageEvent) => { setMessages((prev) => prev.filter((message) => message.id !== deleteMessageEvent.id)); }); return () => { // ... unsubscribeOnMessageDeleted(); }; // ...

Agora é possível excluir usuários de uma sala de chat na sua aplicação de chat.

Próximas etapas

A título de experimento, tente implementar outras ações em uma sala, como desconectar um outro usuário.