SDK per la messaggistica per client di Chat IVS - Tutorial per JavaScript, parte 2: messaggi ed eventi - Amazon IVS

SDK per la messaggistica per client di Chat IVS - Tutorial per JavaScript, parte 2: messaggi ed eventi

Questa seconda e ultima parte del tutorial è suddivisa in diverse sezioni:

Nota: in alcuni casi, gli esempi di codice per JavaScript e TypeScript sono identici, quindi vengono combinati.

Prerequisito

Assicurati di aver completato la prima parte di questo tutorial, Chat room.

Sottoscrizione a eventi di messaggi di chat

L'istanza ChatRoom utilizza gli eventi per comunicare quando si verificano eventi in una chat room. Per iniziare a implementare l'esperienza di chat, devi mostrare ai tuoi utenti quando altri inviano un messaggio nella stanza a cui sono connessi.

Da qui, puoi effettuare la sottoscrizione a eventi di messaggistica della chat Successivamente, ti mostreremo come aggiornare un elenco di messaggi da te creato che viene aggiornato con ogni messaggio/evento.

Nell'App, all'interno dell'hook useEffect, sottoscrivi tutti gli eventi di messaggistica:

TypeScript/JavaScript:

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

Visualizzazione dei messaggi ricevuti

La ricezione di messaggi è una parte fondamentale dell'esperienza di chat. Utilizzando l'SDK JS Chat, puoi configurare il tuo codice per ricevere facilmente eventi da altri utenti connessi a una chat room.

Successivamente, ti mostreremo come eseguire azioni in una chat room sfruttando i componenti qui creati.

Nella tua App, definisci uno stato denominato messages con un tipo di array ChatMessage denominato 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([]); //... }

Successivamente, nella funzione dell'ascoltatore message, aggiungi message all'array messages:

TypeScript/JavaScript:

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

Di seguito esaminiamo le attività da completare per mostrare i messaggi ricevuti:

Creazione di un componente di messaggio

Il componente Message è responsabile della visualizzazione del contenuto di un messaggio ricevuto dalla chat room. In questa sezione, crei un componente di messaggi per il rendering di singoli messaggi di chat nell'App.

Crea un nuovo file nella directory src e chiamalo Message. Inserisci il tipo ChatMessage di questo componente e passa la stringa content dalle proprietà ChatMessage per visualizzare il testo del messaggio ricevuto dagli ascoltatori dei messaggi della chat room. Nella struttura di navigazione del progetto, passa a 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, }, });

Suggerimento: utilizza questo componente per archiviare diverse proprietà da rappresentare nelle righe dei messaggi, ad esempio URL di avatar, nomi utente e timestamp del momento in cui è stato inviato il messaggio.

Riconoscimento dei messaggi inviati dall'utente corrente

Per riconoscere il messaggio inviato dall'utente corrente, modifichiamo il codice e creiamo un contesto React per memorizzare l'userId dell'utente corrente.

Crea un nuovo file nella directory src e chiamalo 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;

Nota: qui abbiamo usato l'hook useState per memorizzare il valore userId. In futuro potrai utilizzare setUserId per modificare il contesto dell'utente o per scopi di accesso.

Sostituisci, quindi, userId nel primo parametro passato a tokenProvider utilizzando il contesto creato in precedenza. Assicurati di aggiungere la funzionalità SEND_MESSAGE al tuo provider di token, come specificato di seguito, in quanto è necessaria per l'invio di messaggi:

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']), }), ); // ... }

Nel tuo componente Message, usa la variabile UserContext creata in precedenza, dichiara la variabile isMine, associa userId del mittente con userId del contesto e applica diversi stili di messaggi per l'utente corrente.

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', }, });

Rendering di un elenco di messaggi di chat

A questo punto, elenca i messaggi utilizzando il 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> ); // ...

Ora tutti i pezzi del puzzle per l'App sono a posto e puoi iniziare a renderizzare i messaggi ricevuti dalla chat room. Continua di seguito per scoprire come eseguire azioni in una chat room sfruttando i componenti appena creati.

Esecuzione di azioni in una chat room

L'invio di messaggi e l'esecuzione delle azioni dei moderatori sono alcune delle principali modalità di interazione con una chatroom. Qui imparerai come utilizzare vari oggetti di richiesta chat per eseguire azioni comuni in Chatterbox, ad esempio l'invio di un messaggio, l'eliminazione di un messaggio e la disconnessione di altri utenti.

Tutte le azioni in una chat room seguono uno schema comune: per ogni azione eseguita in una chat room, esiste un oggetto di richiesta corrispondente. Per ogni richiesta è presente un oggetto di risposta corrispondente che si riceve alla conferma della richiesta.

Se ai tuoi utenti sono offerte le funzionalità corrette quando crei un token di chat, possono eseguire correttamente le azioni corrispondenti utilizzando gli oggetti della richiesta per vedere quali richieste puoi eseguire in una chatroom.

Di seguito, spieghiamo come inviare un messaggio ed eliminare un messaggio.

Invio di un messaggio

La classe SendMessageRequest consente l'invio di messaggi in una chat room. Qui puoi modificare la tua App per inviare una richiesta di messaggio utilizzando il componente che hai creato in Creazione dell'input di un messaggio (nella parte 1 di questo tutorial).

Per iniziare, definisci una nuova proprietà booleana denominata isSending con l'hook useState. Usa questa nuova proprietà per attivare lo stato disabilitato dell'elemento button usando la costante isSendDisabled. Nel gestore eventi per il tuo SendButton, cancella il valore per messageToSend e imposta isSending su true.

Poiché effettuerai una chiamata API da questo pulsante, l'aggiunta della proprietà booleana isSending consente di evitare che si verifichino più chiamate API contemporaneamente, disabilitando le interazioni utente con il SendButton fino al completamento della richiesta.

Nota: l'invio di messaggi funziona solo se hai aggiunto la funzionalità SEND_MESSAGE al tuo provider di token, come descritto sopra in Riconoscimento dei messaggi inviati dall'utente corrente.

TypeScript/JavaScript:

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

Prepara la richiesta creando una nuova istanza SendMessageRequest passando il contenuto del messaggio al costruttore. Dopo aver impostato gli stati isSending e messageToSend, chiama il metodo sendMessage, che invia la richiesta alla chat room. Infine, deseleziona il flag isSending quando ricevi la conferma o il rifiuto della richiesta.

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); } }; // ...

Dai una chance a Chatterbox: prova a inviare un messaggio creandone una bozza con MessageBar e toccando quindi SendButton. Dovresti vedere il messaggio inviato renderizzato all'interno del MessageList creato in precedenza.

Eliminazione di un messaggio

Per eliminare un messaggio da una chat room, è necessario disporre delle funzionalità adeguate. Le funzionalità vengono concesse durante l'inizializzazione del token di chat utilizzato per l'autenticazione in una chat room. Ai fini di questa sezione, ServerApp della sezione Configurazione di un server di autenticazione/autorizzazione locale (nella parte 1 di questo tutorial) consente di specificare le funzionalità dei moderatori. Questa operazione viene eseguita nell'app utilizzando l'oggetto tokenProvider creato in Creazione di un provider di token (anch'esso nella parte 1 del tutorial).

Qui puoi modificare il Message aggiungendo una funzione per eliminare il messaggio.

Innanzitutto, apri App.tsx e aggiungi la funzionalità DELETE_MESSAGE (capabilities è il secondo parametro della funzione tokenProvider).

Nota: in questo modo ServerApp informa le API di IVS Chat che l'utente associato al token di chat risultante può eliminare i messaggi in una chat room. In una situazione reale, probabilmente avrai una logica di backend più complessa per gestire le funzionalità degli utenti nell'infrastruttura della tua app server.

TypeScript/JavaScript:

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

Nei passaggi successivi, aggiorni il tuo Message in modo da visualizzare un pulsante di eliminazione.

Definisci una nuova funzione chiamata onDelete che accetta una stringa come uno dei suoi parametri e restituisce Promise. Per il parametro string, inserisci l'ID del messaggio del 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', }, });

Successivamente, aggiorna il renderItem per riflettere le ultime modifiche apportate al componente FlatList.

Quindi, in App definisci una funzione denominata handleDeleteMessage e passala alla proprietà 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]); // ...

Prepara una richiesta creando una nuova istanza di DeleteMessageRequest, passando l'ID messaggio pertinente al parametro del costruttore e una chiamata deleteMessage che accetti la richiesta preparata sopra:

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); }; // ...

Successivamente, aggiorna lo stato di messages in modo che rifletta un nuovo elenco di messaggi che omette il messaggio appena eliminato.

Nell'hook useEffect, ascolta l'evento messageDelete e aggiorna il tuo array di stato messages eliminando il messaggio con un ID corrispondente al parametro message.

Nota: l'evento messageDelete potrebbe essere generato quando i messaggi vengono eliminati dall'utente corrente o da qualsiasi altro utente presente nella stanza. Gestirlo nel gestore eventi (anziché accanto alla richiesta deleteMessage) consente di unificare la gestione dell'eliminazione dei messaggi.

TypeScript/JavaScript:

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

A questo punto puoi eliminare gli utenti da una chat room nella tua app di chat.

Fasi successive

Come esperimento, prova a implementare altre azioni in una stanza, ad esempio la disconnessione di un altro utente.