

# SDK de messagerie client de chat IVS
<a name="chat-sdk"></a>

Le kit SDK de messagerie client Amazon Interactive Video Services (IVS) Chat est destiné aux développeurs qui créent des applications avec Amazon IVS. Ce kit SDK est conçu pour tirer parti de l'architecture Amazon IVS et fera l'objet de mises à jour, en plus d'Amazon IVS Chat. En tant que kit SDK natif, il est conçu pour minimiser l'impact sur les performances de votre application et sur les appareils avec lesquels vos utilisateurs accèdent à votre application.

## Exigences de la plateforme
<a name="chat-sdk-platform-requirements"></a>

### Navigateurs de bureau
<a name="chat-desktop-browsers"></a>


| Navigateur | Versions prises en charge | 
| --- | --- | 
| Chrome | Deux versions principales (la version actuelle et la version la plus récente) | 
| Edge | Deux versions principales (la version actuelle et la version la plus récente) | 
| Firefox | Deux versions principales (la version actuelle et la version la plus récente) | 
| Opera | Deux versions principales (la version actuelle et la version la plus récente) | 
| Safari | Deux versions principales (la version actuelle et la version la plus récente) | 

### Navigateurs mobiles
<a name="chat-mobile-browsers"></a>


| Navigateur | Versions prises en charge | 
| --- | --- | 
| Chrome pour Android | Deux versions principales (la version actuelle et la version la plus récente) | 
| Firefox pour Android | Deux versions principales (la version actuelle et la version la plus récente) | 
| Opera pour Android | Deux versions principales (la version actuelle et la version la plus récente) | 
| WebView pour Android | Deux versions principales (la version actuelle et la version la plus récente) | 
| Samsung Internet | Deux versions principales (la version actuelle et la version la plus récente) | 
| Safari pour iOS | Deux versions principales (la version actuelle et la version la plus récente) | 

### Plateformes natives
<a name="chat-native-platforms"></a>


| Plateforme | Versions prises en charge | 
| --- | --- | 
| Android | 5.0 et versions ultérieures | 
| iOS |  13.0 et versions ultérieures  | 

## Support
<a name="chat-sdk-support"></a>

Si vous rencontrez une erreur ou un autre problème avec votre salle de chat, déterminez l'identifiant unique de salle via l'API IVS Chat (voir [ListRooms](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_ListRooms.html)).

Partagez cet identifiant de salle de chat avec l'équipe AWS Support. Il lui permettra d’obtenir des informations pour aider à résoudre votre problème.

**Remarque :** veuillez consulter la rubrique [Notes de mise à jour Chat Amazon IVS](release-notes.md) pour connaître les versions disponibles et les problèmes résolus. Le cas échéant, avant de contacter le support technique, mettez à jour la version du kit SDK et vérifiez si cela résout votre problème.

### Gestion des versions
<a name="chat-sdk-support-versioning"></a>

Les kits SDK de messagerie client Amazon IVS Chat sont basés sur la [gestion sémantique de version](https://semver.org/).

Pour ce sujet, supposons que :
+ la dernière version est la version 4.1.3 ;
+ la dernière version de la version majeure précédente est la version 3.2.4 ;
+ la dernière version de la version 1.x est la version 1.5.6.

De nouvelles fonctions rétrocompatibles sont ajoutées en tant que versions mineures de la dernière version. Dans ce cas, la prochaine série de nouvelles fonctions sera ajoutée dans la version 4.2.0.

Des corrections de bogues mineurs rétrocompatibles sont ajoutées en tant que versions de correctifs de la dernière version. Ici, la prochaine série de corrections de bogues mineurs sera ajoutée en tant que version 4.1.4.

Les corrections de bogues majeurs rétrocompatibles sont traitées différemment. Elles sont ajoutées à plusieurs versions :
+ Version de correctifs de la dernière version. Ici, il s’agit de la version 4.1.4.
+ Version de correctifs de la version mineure précédente. Ici, il s’agit de la version 3.2.5.
+ Version de correctifs de la dernière version 1.x. Ici, il s’agit de la version 1.5.7.

Les principales corrections de bogues sont définies par l’équipe produit d’Amazon IVS. Des exemples typiques sont les mises à jour de sécurité critiques et d’autres correctifs nécessaires pour les clients.

**Remarque :** dans les exemples ci-dessus, les versions publiées s’incrémentent sans ignorer de numéros (par exemple, de 4.1.3 à 4.1.4). En réalité, un ou plusieurs numéros de correctifs peuvent rester internes et ne pas être publiés, de sorte que la version publiée peut s’incrémenter de 4.1.3 à 4.1.6, par exemple.

En outre, la version 1.x sera prise en charge jusqu'à la fin de 2023 ou à la sortie de la version 3.x, selon la situation qui survient en dernier.

## API Amazon IVS Chat
<a name="chat-sdk-chat-apis"></a>

Côté serveur (non géré par les kits SDK), il existe deux API, chacune ayant ses propres responsabilités :
+ **Plan de données** : l'[API de messagerie IVS Chat](https://docs.aws.amazon.com//ivs/latest/chatmsgapireference/welcome.html) est une API WebSockets conçue pour être utilisée par des applications frontend (iOS, Android, macOS, etc.) qui sont pilotées par un schéma d'authentification basé sur des jetons. À l'aide d'un jeton de chat généré précédemment, vous vous connectez à des salles de chat déjà existantes avec cette API.

  *Les kits SDK de messagerie client Amazon IVS Chat sont uniquement concernés par le plan de données. Les kits SDK supposent que vous générez déjà des jetons de chat via votre backend. La récupération de ces jetons est supposée être gérée par votre application frontend, et non par les kits SDK.*
+ **Plan de contrôle** : l'[API du plan de contrôle IVS Chat](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/Welcome.html) fournit une interface pour que vos *applications backend* puissent gérer et créer des salles de chat ainsi que les utilisateurs qui les rejoignent. Considérez-la comme le panneau d'administration de l'expérience de chat de votre application, géré par *votre backend*. Certaines opérations du plan de contrôle sont responsables de la création du *jeton de chat* nécessaire au plan de données pour s’authentifier auprès d’une salle de chat.

  **Important :** *les kits SDK de messagerie client Chat IVS n’appellent aucune opération du plan de contrôle. Votre backend doit être configuré pour créer des jetons de chat pour vous. Votre application frontend doit communiquer avec votre backend pour récupérer ce jeton de chat.*

# Kit SDK de messagerie client Chat IVS: guide Android
<a name="chat-sdk-android"></a>

Le kit SDK de messagerie client Amazon Interactive Video (IVS) Chat pour Android fournit des interfaces qui vous permettent d'intégrer facilement notre [API de messagerie IVS Chat](https://docs.aws.amazon.com//ivs/latest/chatmsgapireference/welcome.html) sur les plateformes utilisant Android.

Le package `com.amazonaws:ivs-chat-messaging` implémente l’interface décrite dans ce document.

**Dernière version du SDK de messagerie client IVS Chat pour Android :** 1.1.0 ([notes de mise à jour](https://docs.aws.amazon.com//ivs/latest/ChatUserGuide/release-notes.html#jan31-23))

**Documentation de référence : ** pour plus d'informations sur les méthodes les plus importantes disponibles dans le SDK de messagerie client Chat Amazon IVS pour Android, veuillez consulter la documentation de référence à l'adresse : [https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.1.0/](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.1.0/)

**Exemple de code :** veuillez consulter le référentiel d'exemples Android sur GitHub : [https://github.com/aws-samples/amazon-ivs-chat-for-android-demo](https://github.com/aws-samples/amazon-ivs-chat-for-android-demo)

**Exigences de la plateforme :** Android 5.0 (API de niveau 21) ou une version ultérieure est nécessaire pour le développement.

# Démarrez avec le SDK de messagerie client Chat Amazon IVS pour Android
<a name="chat-android-getting-started"></a>

Avant de commencer, vous devez être familiarisé avec la [Mise en route avec Chat Amazon IVS](getting-started-chat.md).

## Ajouter le package
<a name="chat-android-add-package"></a>

Ajoutez `com.amazonaws:ivs-chat-messaging` à vos dépendances `build.gradle` :

```
dependencies {
   implementation 'com.amazonaws:ivs-chat-messaging'
}
```

## Ajouter des règles ProGuard
<a name="chat-android-proguard-rules"></a>

Ajoutez les entrées suivantes à votre fichier de règles R8/ProGuard (`proguard-rules.pro`) :

```
-keep public class com.amazonaws.ivs.chat.messaging.** { *; }
-keep public interface com.amazonaws.ivs.chat.messaging.** { *; }
```

## Configuration de votre backend
<a name="chat-android-setup-backend"></a>

Cette intégration nécessite des points de terminaison sur votre serveur qui communiquent avec l'[API Amazon IVS](https://docs.aws.amazon.com//ivs/latest/LowLatencyAPIReference/Welcome.html). Utilisez les [bibliothèques AWS officielles](https://aws.amazon.com/developer/tools/) pour accéder à l'API Amazon IVS depuis votre serveur. Elles sont accessibles dans plusieurs langues depuis les packages publics, par exemple, node.js et Java.

Ensuite, créez un point de terminaison de serveur qui communique avec l'[API Chat Amazon IVS](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/Welcome.html) et crée un jeton.

## Configurer une connexion au serveur
<a name="chat-android-setup-server"></a>

Créez une méthode qui considère `ChatTokenCallback` comme un paramètre et récupère un jeton de chat depuis votre backend. Transmettez ce jeton à la méthode `onSuccess` du rappel. En cas d'erreur, transmettez l'exception à la méthode `onError` du rappel. Ceci est nécessaire pour instancier la principale entité `ChatRoom` lors de l'étape suivante.

Vous trouverez ci-dessous un exemple de code qui implémente ce qui précède à l'aide d'un appel `Retrofit`.

```
// ...

private fun fetchChatToken(callback: ChatTokenCallback) {
    apiService.createChatToken(userId, roomId).enqueue(object : Callback<ChatToken> {
        override fun onResponse(call: Call<ExampleResponse>, response: Response<ExampleResponse>) {
            val body = response.body()
            val token = ChatToken(
                body.token,
                body.sessionExpirationTime,
                body.tokenExpirationTime
            )
            callback.onSuccess(token)
        }

        override fun onFailure(call: Call<ChatToken>, throwable: Throwable) {
            callback.onError(throwable)
        }
    })
}
// ...
```

# Utilisation de l'SDK de messagerie client Chat Amazon IVS pour Android
<a name="chat-android-using-sdk"></a>

Ce document explique les étapes nécessaires à l'utilisation de l'SDK de messagerie client Chat Amazon IVS pour Android.

## Initialiser une instance de salle de chat
<a name="chat-android-initialize-room"></a>

Créez une instance de la classe `ChatRoom`. Cela nécessite de transmettre `regionOrUrl`, qui correspond généralement à la région AWS dans laquelle votre salle de chat est hébergée, et `tokenProvider` qui est la méthode de récupération de jetons créée à l'étape précédente.

```
val room = ChatRoom(
    regionOrUrl = "us-west-2",
    tokenProvider = ::fetchChatToken
)
```

Ensuite, créez un objet écouteur qui implémentera des gestionnaires pour les événements liés au chat et attribuez-le à la propriété `room.listener` :

```
private val roomListener = object : ChatRoomListener {
    override fun onConnecting(room: ChatRoom) {
      // Called when room is establishing the initial connection or reestablishing connection after socket failure/token expiration/etc
    }

    override fun onConnected(room: ChatRoom) {
        // Called when connection has been established
    }

    override fun onDisconnected(room: ChatRoom, reason: DisconnectReason) {
        // Called when a room has been disconnected
    }

    override fun onMessageReceived(room: ChatRoom, message: ChatMessage) {
        // Called when chat message has been received
    }

    override fun onEventReceived(room: ChatRoom, event: ChatEvent) {
        // Called when chat event has been received
    }

    override fun onDeleteMessage(room: ChatRoom, event: DeleteMessageEvent) {
       // Called when DELETE_MESSAGE event has been received
    }
}

val room = ChatRoom(
    region = "us-west-2",
    tokenProvider = ::fetchChatToken
)

room.listener = roomListener // <- add this line

// ...
```

La dernière étape de l'initialisation de base consiste à se connecter à la salle spécifique en établissant une connexion WebSocket. Pour ce faire, appelez la méthode `connect()` au sein de l'instance de la salle. Nous vous recommandons de procéder selon la méthode du cycle de vie `onResume() ` pour vous assurer qu'elle conserve une connexion si votre application reprend en arrière-plan.

```
room.connect()
```

Le kit SDK tentera d'établir une connexion à une salle de chat codée dans le jeton de chat reçu de votre serveur. En cas d'échec, il tentera de se reconnecter le nombre de fois spécifié dans l'instance de la salle.

## Effectuer des actions dans une salle de chat
<a name="chat-android-room-actions"></a>

La classe `ChatRoom` contient des actions permettant d'envoyer et de supprimer des messages ainsi que de déconnecter d'autres utilisateurs. Ces actions acceptent un paramètre de rappel facultatif qui vous permet de recevoir des notifications de confirmation ou de rejet de demande.

### Envoi d'un message
<a name="chat-android-room-actions-send-message"></a>

Pour cette demande, la fonctionnalité `SEND_MESSAGE` doit être encodée dans votre jeton de chat.

Pour déclencher une demande send-message :

```
val request = SendMessageRequest("Test Echo")
room.sendMessage(request)
```

Pour obtenir une confirmation/un rejet de la demande, fournissez un rappel comme second paramètre :

```
room.sendMessage(request, object : SendMessageCallback {
   override fun onConfirmed(request: SendMessageRequest, response: ChatMessage) {
      // Message was successfully sent to the chat room.
   }
   override fun onRejected(request: SendMessageRequest, error: ChatError) {
      // Send-message request was rejected. Inspect the `error` parameter for details.
   }
})
```

### Supprimer un message
<a name="chat-android-room-actions-delete-message"></a>

Pour cette demande, la fonctionnalité DELETE\$1MESSAGE doit être encodée dans votre jeton de chat.

Pour déclencher une demande delete-message :

```
val request = DeleteMessageRequest(messageId, "Some delete reason")
room.deleteMessage(request)
```

Pour obtenir une confirmation/un rejet de la demande, fournissez un rappel comme second paramètre :

```
room.deleteMessage(request, object : DeleteMessageCallback {
   override fun onConfirmed(request: DeleteMessageRequest, response: DeleteMessageEvent) {
      // Message was successfully deleted from the chat room.
   }
   override fun onRejected(request: DeleteMessageRequest, error: ChatError) {
      // Delete-message request was rejected. Inspect the `error` parameter for details.
   }
})
```

### Déconnecter un autre utilisateur
<a name="chat-android-room-actions-disconnect-user"></a>

Pour cette demande, la fonctionnalité `DISCONNECT_USER` doit être encodée dans votre jeton de chat.

Pour déconnecter un autre utilisateur à des fins de modération :

```
val request = DisconnectUserRequest(userId, "Reason for disconnecting user")
room.disconnectUser(request)
```

Pour obtenir la confirmation/le rejet de la demande, fournissez un rappel comme second paramètre :

```
room.disconnectUser(request, object : DisconnectUserCallback {
   override fun onConfirmed(request: SendMessageRequest, response: ChatMessage) {
      // User was disconnected from the chat room.
   }
   override fun onRejected(request: SendMessageRequest, error: ChatError) {
      // Disconnect-user request was rejected. Inspect the `error` parameter for details.
   }
})
```

## Déconnexion d'une salle de chat
<a name="chat-android-disconnect-room"></a>

Pour fermer votre connexion à la salle de chat, appelez la méthode `disconnect()` sur l'instance de la salle :

```
room.disconnect()
```

Étant donné que la connexion WebSocket cesse de fonctionner peu de temps après que l'application passe en arrière-plan, nous vous recommandons de vous connecter/déconnecter manuellement lors de la transition depuis/vers un état d'arrière-plan. Pour ce faire, associez l'appel `room.connect()` de la méthode du cycle de vie `onResume()`, sur `Activity` ou `Fragment` Android, avec un appel `room.disconnect()` de la méthode du cycle de vie `onPause()`.

# SDK de messagerie client Chat IVS : didacticiel Android, partie 1 : salles de chat
<a name="chat-sdk-android-tutorial-chat-rooms"></a>

Il s'agit de la première partie d'un didacticiel en deux volets. Vous apprendrez les bases de l'utilisation du SDK de messagerie Chat Amazon IVS en créant une application Android entièrement fonctionnelle à l'aide du langage de programmation [Kotlin](https://kotlinlang.org/). Nous appelons l'application *Chatterbox*.

Avant de commencer le module, prenez quelques minutes pour vous familiariser avec les prérequis, les concepts clés des jetons de discussion et le serveur principal nécessaire à la création de salles de chat.

Ces didacticiels sont conçus pour les développeurs Android expérimentés qui découvrent le SDK de messagerie d'IVS Chat. Vous devrez être à l'aise avec le langage de programmation Kotlin et la création d'interfaces utilisateur sur la plateforme Android.

Cette première partie du didacticiel est divisée en plusieurs sections :

1. [Configurer un serveur d'authentification/d'autorisation local](#chat-android-rooms-auth-server)

1. [Créer un projet Chatterbox](#chat-android-rooms-chatterbox)

1. [Se connecter à une salle de chat et observer les mises à jour de la connexion](#chat-android-rooms-connect-state)

1. [Créer un fournisseur de jetons](#chat-android-rooms-token-provider)

1. [Étapes suivantes](#chat-android-rooms-next-steps)

Pour une documentation complète sur le SDK, commencez par le [SDK de messagerie client Chat Amazon IVS](chat-sdk.md) (ici dans le *Guide de l'utilisateur Chat Amazon IVS*) et la [Messagerie client de chat : référence du SDK pour Android](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/) (sur GitHub).

## Conditions préalables
<a name="chat-android-rooms-prerequisites"></a>
+ Familiarisez-vous avec Kotlin et la création d'applications sur la plateforme Android. Si vous n'êtes pas familiarisé avec la création d'applications pour Android, découvrez les bases dans le guide [Créer votre première application](https://developer.android.com/codelabs/basic-android-kotlin-compose-first-app#0) pour les développeurs Android.
+ Lisez attentivement et découvrez [Mise en route avec le chat Amazon IVS](getting-started-chat.md).
+ Créez un utilisateur AWS IAM avec les fonctionnalités `CreateChatToken` et `CreateRoom` définies dans une politique IAM existante. (Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md).)
+ Assurez-vous que les clés secrètes et d'accès de cet utilisateur sont stockées dans un fichier d'informations d'identification AWS. Pour obtenir des instructions, consultez le [Guide de l'utilisateur de l'interface de ligne de commande AWS](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) (en particulier les [paramètres de configuration et de fichier d'informations d'identification](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)).
+ Créez une salle de chat et enregistrez son ARN. Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md). (Si vous n'enregistrez pas l'ARN, vous pourrez le consulter ultérieurement à l'aide de la console ou de l'API Chat.)

## Configurer un serveur d'authentification/d'autorisation local
<a name="chat-android-rooms-auth-server"></a>

Votre serveur backend est chargé à la fois de créer des salles de chat et de générer les jetons de chat nécessaires au SDK de chat IVS pour Android pour authentifier et autoriser vos clients à accéder à vos salles de chat.

Consultez la section [Créer un jeton de chat](getting-started-chat-auth.md) dans *Mise en route avec Chat Amazon IVS*. Comme le montre l'organigramme, votre code côté serveur est chargée de créer un jeton de chat. Cela signifie que votre application doit fournir ses propres moyens de générer un jeton de chat en demandant un jeton à votre application côté serveur.

Nous utilisons l'infrastructure [Ktor](https://ktor.io/) pour créer un serveur local en direct qui gère la création de jetons de chat à l'aide de votre environnement AWS local.

À ce stade, nous nous attendons à ce que vos informations d'identification AWS soient correctement configurées. Pour savoir comment procéder, consultez [Configuration des informations d'identification et de la région AWS pour le développement](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-credentials.html).

Créez un nouveau répertoire et appelez-le `chatterbox` et, à l'intérieur, un autre, appelé `auth-server`. 

La structure de notre serveur sera la suivante :

```
- auth-server
  - src
    - main
      - kotlin
        - com
          - chatterbox
            - authserver
              - Application.kt
       - resources
         - application.conf
         - logback.xml
   - build.gradle.kts
```

*Remarque : vous pouvez directement copier/coller le code ici dans les fichiers référencés.*

Ensuite, nous ajoutons toutes les dépendances et tous les plugins nécessaires au fonctionnement de notre serveur d'authentification :

**Script Kotlin** :

```
// ./auth-server/build.gradle.kts

plugins {
   application
   kotlin("jvm")
   kotlin("plugin.serialization").version("1.7.10")
}

application {
   mainClass.set("io.ktor.server.netty.EngineMain")
}

dependencies {
   implementation("software.amazon.awssdk:ivschat:2.18.1")
   implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20")

   implementation("io.ktor:ktor-server-core:2.1.3")
   implementation("io.ktor:ktor-server-netty:2.1.3")
   implementation("io.ktor:ktor-server-content-negotiation:2.1.3")
   implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.3")

   implementation("ch.qos.logback:logback-classic:1.4.4")
}
```

Nous devons maintenant configurer la fonctionnalité de journalisation pour le serveur d'authentification. (Pour plus d'informations, veuillez consulter [Configure logger](https://ktor.io/docs/logging.html#configure-logger).)

**XML** :

```
// ./auth-server/src/main/resources/logback.xml

<configuration>
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
   </appender>
   <root level="trace">
      <appender-ref ref="STDOUT"/>
   </root>
   <logger name="org.eclipse.jetty" level="INFO"/>
   <logger name="io.netty" level="INFO"/>
</configuration>
```

Le serveur [Ktor](https://ktor.io/docs/welcome.html) nécessite des paramètres de configuration, qu'il charge automatiquement à partir du fichier `application.*` du répertoire `resources`. Nous les ajoutons donc également. (Pour plus d'informations, consultez [Configuration in a file](https://ktor.io/docs/configurations.html#configuration-file).)

**HOCON** :

```
// ./auth-server/src/main/resources/application.conf

ktor {
   deployment {
      port = 3000
   }
   application {
      modules = [ com.chatterbox.authserver.ApplicationKt.main ]
   }
}
```

Enfin, implémentons notre serveur :

**Kotlin** :

```
// ./auth-server/src/main/kotlin/com/chatterbox/authserver/Application.kt

package com.chatterbox.authserver

import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import software.amazon.awssdk.services.ivschat.IvschatClient
import software.amazon.awssdk.services.ivschat.model.CreateChatTokenRequest

@Serializable
data class ChatTokenParams(var userId: String, var roomIdentifier: String)

@Serializable
data class ChatToken(
   val token: String,
   val sessionExpirationTime: String,
   val tokenExpirationTime: String,
)

fun Application.main() {
   install(ContentNegotiation) {
      json(Json)
   }

   routing {
      post("/create_chat_token") {
         val callParameters = call.receive<ChatTokenParams>()
         val request = CreateChatTokenRequest.builder().roomIdentifier(callParameters.roomIdentifier)
            .userId(callParameters.userId).build()
         val token = IvschatClient.create()
            .createChatToken(request)

         call.respond(
            ChatToken(
                token.token(),
                token.sessionExpirationTime().toString(),
                token.tokenExpirationTime().toString()
            )
         )
      }
   }
}
```

## Créer un projet Chatterbox
<a name="chat-android-rooms-chatterbox"></a>

Pour créer un projet Android, installez et ouvrez [Android Studio](https://developer.android.com/studio).

Suivez les étapes répertoriées dans le guide officiel Android [Créer un projet](https://developer.android.com/studio/projects/create-project).
+ Dans [Choisir le type de projet](https://developer.android.com/studio/projects/create-project), choisissez le modèle de projet **Activité vide** pour notre application Chatterbox.
+ Dans [Configurer votre projet](https://developer.android.com/studio/projects/create-project#configure), choisissez les valeurs suivantes pour les champs de configuration :
  + **Nom** : My App
  + **Nom de package** : com.chatterbox.myapp
  + **Emplacement d'enregistrement** : pointez sur le répertoire `chatterbox` créé à l'étape précédente
  + **Langage** : Kotlin
  + **Niveau d'API minimum** : API 21 : Android 5.0 (Lollipop)

Après avoir correctement spécifié tous les paramètres de configuration, la structure de nos fichiers dans le dossier `chatterbox` doit ressembler à ce qui suit :

```
- app
  - build.gradle
  ...
- gradle
- .gitignore
- build.gradle
- gradle.properties
- gradlew
- gradlew.bat
- local.properties
- settings.gradle
- auth-server
  - src
    - main
      - kotlin
        - com
          - chatterbox
            - authserver
              - Application.kt
       - resources
         - application.conf
         - logback.xml
   - build.gradle.kts
```

Maintenant que nous avons un projet Android fonctionnel, nous pouvons ajouter [com.amazonaws:ivs-chat-messaging](https://mvnrepository.com/artifact/com.amazonaws/ivs-chat-messaging) à nos dépendances `build.gradle`. (Pour plus d'informations sur la boîte à outils de génération [Gradle](https://gradle.org/), voir [Configurer votre build](https://developer.android.com/build).)

**Remarque** : en haut de chaque extrait de code, il y a un chemin vers le fichier dans lequel vous devez apporter des modifications à votre projet. Le chemin est relatif par rapport à la racine du projet.

*Dans le code ci-dessous, remplacez `<version>` par le numéro de version actuel du SDK de chat pour Android (par exemple, 1.0.0)*.

**Kotlin** :

```
// ./app/build.gradle

plugins {
// ...
}

android {
// ...
}

dependencies {
   implementation("com.amazonaws:ivs-chat-messaging:<version>")
// ...
}
```

Une fois la nouvelle dépendance ajoutée, exécutez **Synchroniser le projet avec les fichiers Gradle** dans Android Studio pour synchroniser le projet avec la nouvelle dépendance. (Pour de plus amples informations, veuillez consulter la section [Ajouter des dépendances de build](https://developer.android.com/build/dependencies).)

Pour exécuter facilement notre serveur d'authentification (créé dans la section précédente) à partir de la racine du projet, nous l'incluons en tant que nouveau module dans `settings.gradle`. (Pour plus d'informations, voir [Structurer et construire un composant de logiciel avec Gradle](https://docs.gradle.org/current/userguide/multi_project_builds.html).)

**Script Kotlin** :

```
// ./settings.gradle

// ...

rootProject.name = "Chatterbox"
include ':app'
include ':auth-server'
```

À présent, comme `auth-server` est inclus dans le projet Android, vous pouvez exécuter le serveur d'authentification à l'aide de la commande suivante depuis la racine du projet :

**Shell** :

```
./gradlew :auth-server:run
```

## Se connecter à une salle de chat et observer les mises à jour de la connexion
<a name="chat-android-rooms-connect-state"></a>

Pour ouvrir une connexion à une salle de chat, nous utilisons le [rappel du cycle de vie de l'activité onCreate()](https://developer.android.com/guide/components/activities/activity-lifecycle), qui se déclenche lorsque l'activité est créée pour la première fois. Le [constructeur ChatRoom](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html) nous oblige à fournir `region` et `tokenProvider` pour lancer une connexion à une salle.

**Remarque** : la fonction `fetchChatToken` présentée dans l'extrait ci-dessous sera implémentée dans [la section suivante](#chat-android-rooms-token-provider).

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp

// ...
import androidx.appcompat.app.AppCompatActivity
// ...

// AWS region of the room that was created in Getting Started with Amazon IVS Chat
const val REGION = "us-west-2"

class MainActivity : AppCompatActivity() {
    private var room: ChatRoom? = null
    // ...

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      // Create room instance
      room = ChatRoom(REGION, ::fetchChatToken)
   }

// ...
}
```

L'affichage et la réaction aux modifications de la connexion à une salle de chat est un élément essentiel pour la création d'une application de chat comme `chatterbox`. Avant de pouvoir commencer à interagir avec la salle, nous devons nous abonner aux événements relatifs à l'état de connexion de la salle de chat pour obtenir des mises à jour.

[ChatRoom](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html) attend de nous que nous joignions une implémentation d'[interface ChatRoomListener](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/listener.html) pour signaler les événements du cycle de vie. Pour l'instant, les fonctions d'écouteur enregistreront uniquement les messages de confirmation, lorsqu'ils sont invoqués :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

// ...
package com.chatterbox.myapp
// ...
const val TAG = "IVSChat-App"

class MainActivity : AppCompatActivity() {
// ...

    private val roomListener = object : ChatRoomListener {
        override fun onConnecting(room: ChatRoom) {
            Log.d(TAG, "onConnecting")
        }

        override fun onConnected(room: ChatRoom) {
            Log.d(TAG, "onConnected")
        }

        override fun onDisconnected(room: ChatRoom, reason: DisconnectReason) {
            Log.d(TAG, "onDisconnected $reason")
        }

        override fun onMessageReceived(room: ChatRoom, message: ChatMessage) {
            Log.d(TAG, "onMessageReceived $message")
        }

        override fun onMessageDeleted(room: ChatRoom, event: DeleteMessageEvent) {
            Log.d(TAG, "onMessageDeleted $event")
        }

        override fun onEventReceived(room: ChatRoom, event: ChatEvent) {
            Log.d(TAG, "onEventReceived $event")
        }

        override fun onUserDisconnected(room: ChatRoom, event:    DisconnectUserEvent) {
            Log.d(TAG, "onUserDisconnected $event")
        }
    }
}
```

Maintenant que nous avons implémenté `ChatRoomListener`, nous l'attachons à notre instance de salle :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   binding = ActivityMainBinding.inflate(layoutInflater)
   setContentView(binding.root)

   // Create room instance
   room = ChatRoom(REGION, ::fetchChatToken).apply {
      listener = roomListener
   }
}

private val roomListener = object : ChatRoomListener {
// ...
}
```

Ensuite, nous devons fournir la possibilité de lire l'état de la connexion à la salle. Nous le conserverons dans la [propriété](https://kotlinlang.org/docs/properties.html) `MainActivity.kt` et l'initialiserons à l'état DÉCONNECTÉ par défaut pour les salles (voir `ChatRoom state` dans la [référence du SDK IVS Chat pour Android](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/latest/)). Pour pouvoir maintenir l'état local à jour, nous devons implémenter une fonction de mise à jour de l'état ; appelons-le   `updateConnectionState`:

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

enum class ConnectionState {
   CONNECTED,
   DISCONNECTED,
   LOADING
}

class MainActivity : AppCompatActivity() {
   private var connectionState = ConnectionState.DISCONNECTED
// ...

   private fun updateConnectionState(state: ConnectionState) {
      connectionState = state

      when (state) {
         ConnectionState.CONNECTED -> {
            Log.d(TAG, "room connected")
         }
         ConnectionState.DISCONNECTED -> {
            Log.d(TAG, "room disconnected")
         }
         ConnectionState.LOADING -> {
            Log.d(TAG, "room loading")
         }
      }
   }
}
```

Ensuite, nous intégrons notre fonction de mise à jour de l'état à la propriété [Chatroom.Listener](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/listener.html) :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
// ...

   private val roomListener = object : ChatRoomListener {
      override fun onConnecting(room: ChatRoom) {
         Log.d(TAG, "onConnecting")
         runOnUiThread {
            updateConnectionState(ConnectionState.LOADING)
         }
      }

      override fun onConnected(room: ChatRoom) {
         Log.d(TAG, "onConnected")
         runOnUiThread {
            updateConnectionState(ConnectionState.CONNECTED)
         }
      }

      override fun onDisconnected(room: ChatRoom, reason: DisconnectReason) {
         Log.d(TAG, "[${Thread.currentThread().name}] onDisconnected")
         runOnUiThread {
            updateConnectionState(ConnectionState.DISCONNECTED)
         }
      }
   }
}
```

Maintenant que nous sommes en mesure d'enregistrer, d'écouter et de réagir aux mises à jour d'état de [ChatRoom](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html), il est temps d'initialiser une connexion :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

enum class ConnectionState {
   CONNECTED,
   DISCONNECTED,
   LOADING
}

class MainActivity : AppCompatActivity() {
   private var connectionState = ConnectionState.DISCONNECTED
// ...

   private fun connect() {
      try {
         room?.connect()
      } catch (ex: Exception) {
         Log.e(TAG, "Error while calling connect()", ex)
      }
   }

   private val roomListener = object : ChatRoomListener {
      // ...
      override fun onConnecting(room: ChatRoom) {
         Log.d(TAG, "onConnecting")
         runOnUiThread {
            updateConnectionState(ConnectionState.LOADING)
         }
      }

      override fun onConnected(room: ChatRoom) {
         Log.d(TAG, "onConnected")
         runOnUiThread {
            updateConnectionState(ConnectionState.CONNECTED)
         }
      }
      // ...
   }
}
```

## Créer un fournisseur de jetons
<a name="chat-android-rooms-token-provider"></a>

Il est temps de créer une fonction chargée de créer et de gérer des jetons de chat dans notre application. Dans cet exemple, nous utilisons le [client HTTP Retrofit pour Android](https://square.github.io/retrofit/).

Avant de pouvoir envoyer du trafic réseau, nous devons configurer une configuration de sécurité réseau pour Android. Pour plus d'informations, consultez [Network security configuration](https://developer.android.com/privacy-and-security/security-config).) Nous commençons par ajouter des autorisations réseau au fichier [App Manifest](https://developer.android.com/guide/topics/manifest/manifest-intro). Notez la balise `user-permission` et l'attribut `networkSecurityConfig` ajoutés, qui indiqueront notre nouvelle configuration de sécurité réseau. *Dans le code ci-dessous, remplacez `<version>` par le numéro de version actuel du SDK de chat pour Android (par exemple, 1.0.0)*.

**XML** :

```
// ./app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.chatterbox.myapp">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:fullBackupContent="@xml/backup_rules"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
// ...

// ./app/build.gradle


dependencies {
   implementation("com.amazonaws:ivs-chat-messaging:<version>")
// ...

   implementation("com.squareup.retrofit2:retrofit:2.9.0")
}
```

Déclarez `10.0.2.2` et les domaines `localhost` comme fiables, pour commencer à échanger des messages avec notre backend :

**XML** :

```
// ./app/src/main/res/xml/network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">10.0.2.2</domain>
        <domain includeSubdomains="true">localhost</domain>
    </domain-config>
</network-security-config>
```

Ensuite, nous devons ajouter une nouvelle dépendance, ainsi que l'[ajout d'un convertisseur Gson](https://github.com/square/retrofit/tree/trunk/retrofit-converters/gson) pour analyser les réponses HTTP. *Dans le code ci-dessous, remplacez `<version>` par le numéro de version actuel du SDK de chat pour Android (par exemple, 1.0.0)*.

**Script Kotlin** :

```
// ./app/build.gradle

dependencies {
   implementation("com.amazonaws:ivs-chat-messaging:<version>")
// ...

   implementation("com.squareup.retrofit2:retrofit:2.9.0")
}
```

Pour récupérer un jeton de chat, nous devons effectuer une requête HTTP POST depuis notre application `chatterbox`. Nous définissons la demande dans une interface que Retrofit doit implémenter. (Voir la [documentation de Retrofit](https://square.github.io/retrofit/) (français non garanti). Familiarisez-vous également avec la spécification de l’opération [CreateChatToken](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_CreateChatToken.html#API_CreateChatToken_RequestBody).)

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/network/ApiService.kt

package com.chatterbox.myapp.network
// ...


import androidx.annotation.Keep
import com.amazonaws.ivs.chat.messaging.ChatToken
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST

data class CreateTokenParams(var userId: String, var roomIdentifier: String)

interface ApiService {
   @POST("create_chat_token")
   fun createChatToken(@Body params: CreateTokenParams): Call<ChatToken>
}
```

Maintenant que le réseau est configuré, il est temps d'ajouter une fonction chargée de créer et de gérer notre jeton de chat. Nous l'ajoutons à `MainActivity.kt`, qui a été automatiquement créé lors de la [création](#chat-android-rooms-chatterbox) du projet :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt


package com.chatterbox.myapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.amazonaws.ivs.chat.messaging.*
import com.chatterbox.myapp.network.CreateTokenParams
import com.chatterbox.myapp.network.RetrofitFactory
import retrofit2.Call
import java.io.IOException
import retrofit2.Callback
import retrofit2.Response

// custom tag for logging purposes
const val TAG = "IVSChat-App"

// any ID to be associated with auth token
const val USER_ID = "test user id"
// ID of the room the app wants to access. Must be an ARN. See Amazon Resource Names(ARNs)
const val ROOM_ID = "arn:aws:..."
// AWS region of the room that was created in Getting Started with Amazon IVS Chat
const val REGION = "us-west-2"

class MainActivity : AppCompatActivity() {
   private val service = RetrofitFactory.makeRetrofitService()
   private lateinit var userId: String

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
   }

   private fun fetchChatToken(callback: ChatTokenCallback) {
      val params = CreateTokenParams(userId, ROOM_ID)
      service.createChatToken(params).enqueue(object : Callback<ChatToken> {
         override fun onResponse(call: Call<ChatToken>, response: Response<ChatToken>) {
            val token = response.body()
            if (token == null) {
               Log.e(TAG, "Received empty token response")
               callback.onFailure(IOException("Empty token response"))
               return
            }

            Log.d(TAG, "Received token response $token")
            callback.onSuccess(token)
         }

         override fun onFailure(call: Call<ChatToken>, throwable: Throwable) {
            Log.e(TAG, "Failed to fetch token", throwable)
            callback.onFailure(throwable)
         }
      })
   }
}
```

## Étapes suivantes
<a name="chat-android-rooms-next-steps"></a>

Maintenant que vous avez établi une connexion à la salle de chat, passez à la seconde partie de ce didacticiel Android, [Messages et événements](chat-sdk-android-tutorial-messages-events.md).

# Kit SDK de messagerie client Chat IVS : didacticiel Android, partie 2 : messages et événements
<a name="chat-sdk-android-tutorial-messages-events"></a>

Cette seconde et dernière partie du didacticiel est divisée en plusieurs sections :

1. [Création d'une interface utilisateur pour l'envoi de messages](#chat-android-messages-events-create_ui)

   1. [Mise en page principale de l'interface utilisateur](#create-ui-main-layout)

   1. [cellule de texte abstraite de l'interface utilisateur pour afficher le texte de manière cohérente](#create-ui-text-cell)

   1. [message à gauche de l'interface utilisateur de chat](#create-ui-left-chat-message)

   1. [message à droite de l'interface utilisateur de chat](#create-ui-right-chat-message)

   1. [valeurs de couleur supplémentaires de l'interface utilisateur](#create-ui-color-values)

1. [appliquer la liaison d'affichage](#chat-android-messages-events-view-binding)

1. [Gérer les demandes de messages de chat](#chat-android-messages-events-requests)

1. [Étapes finales](#chat-android-messages-events-final-steps)

Pour une documentation complète sur le SDK, commencez par le [SDK de messagerie client Chat Amazon IVS](chat-sdk.md) (ici dans le *Guide de l'utilisateur Chat Amazon IVS*) et la [Messagerie client de chat : référence du SDK pour Android](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/latest/) (sur GitHub).

## Prérequis
<a name="chat-android-messages-events-prerequisite"></a>

Assurez-vous d'avoir terminé la première partie de ce didacticiel relative aux [Salles de chat](chat-sdk-android-tutorial-chat-rooms.md).

## Création d'une interface utilisateur pour l'envoi de messages
<a name="chat-android-messages-events-create_ui"></a>

Maintenant que nous avons initialisé avec succès la connexion à la salle de chat, il est temps d'envoyer notre premier message. Pour cette fonctionnalité, une interface utilisateur est nécessaire. Nous ajouterons :
+ un bouton `connect/disconnect`
+ une saisie de message avec un bouton `send`
+ une liste de messages dynamiques. Pour la créer, nous utilisons Android Jetpack [RecyclerView](https://developer.android.com/develop/ui/views/layout/recyclerview).

### Mise en page principale de l'interface utilisateur
<a name="create-ui-main-layout"></a>

Consultez les [mises en page](https://developer.android.com/develop/ui/views/layout/declaring-layout) Android Jetpack dans la documentation pour les développeurs Android.

**XML** :

```
// ./app/src/main/res/layout/activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                     xmlns:app="http://schemas.android.com/apk/res-auto"
                                                     xmlns:tools="http://schemas.android.com/tools"
                                                     android:layout_width="match_parent"
                                                     android:layout_height="match_parent">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:app="http://schemas.android.com/apk/res-auto"
                  android:id="@+id/connect_view"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:gravity="center"
                  android:orientation="vertical">

        <androidx.cardview.widget.CardView
                android:id="@+id/connect_button"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity=""
                android:layout_marginStart="16dp"
                android:layout_marginTop="4dp"
                android:layout_marginEnd="16dp"
                android:clickable="true"
                android:elevation="16dp"
                android:focusable="true"
                android:foreground="?android:attr/selectableItemBackground"
                app:cardBackgroundColor="@color/purple_500"
                app:cardCornerRadius="10dp">

            <TextView
                    android:id="@+id/connect_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentEnd="true"
                    android:layout_gravity="center"
                    android:layout_weight="1"
                    android:paddingHorizontal="12dp"
                    android:text="Connect"
                    android:textColor="@color/white"
                    android:textSize="16sp"/>

            <ProgressBar
                    android:id="@+id/activity_indicator"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:layout_gravity="center"
                    android:layout_marginHorizontal="20dp"
                    android:indeterminateOnly="true"
                    android:indeterminateTint="@color/white"
                    android:indeterminateTintMode="src_atop"
                    android:keepScreenOn="true"
                    android:visibility="gone"/>
        </androidx.cardview.widget.CardView>

    </LinearLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/chat_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:visibility="visible"
            tools:context=".MainActivity">

        <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintBottom_toTopOf="@+id/layout_message_input"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent">

            <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recycler_view"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clipToPadding="false"
                    android:paddingTop="70dp"
                    android:paddingBottom="20dp"/>
        </RelativeLayout>

        <RelativeLayout
                android:id="@+id/layout_message_input"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:clipToPadding="false"
                android:drawableTop="@android:color/black"
                android:elevation="18dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent">

            <EditText
                    android:id="@+id/message_edit_text"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_marginStart="16dp"
                    android:layout_toStartOf="@+id/send_button"
                    android:background="@android:color/transparent"
                    android:hint="Enter Message"
                    android:inputType="text"
                    android:maxLines="6"
                    tools:ignore="Autofill"/>

            <Button
                    android:id="@+id/send_button"
                    android:layout_width="84dp"
                    android:layout_height="48dp"
                    android:layout_alignParentEnd="true"
                    android:background="@color/black"
                    android:foreground="?android:attr/selectableItemBackground"
                    android:text="Send"
                    android:textColor="@color/white"
                    android:textSize="12dp"/>
        </RelativeLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.coordinatorlayout.widget.CoordinatorLayout>
```

### cellule de texte abstraite de l'interface utilisateur pour afficher le texte de manière cohérente
<a name="create-ui-text-cell"></a>

**XML** :

```
// ./app/src/main/res/layout/common_cell.xml
   
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/layout_container"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:background="@color/light_gray"
              android:minWidth="100dp"
              android:orientation="vertical">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">

        <TextView
                android:id="@+id/card_message_me_text_view"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_marginBottom="8dp"
                android:maxWidth="260dp"
                android:paddingLeft="12dp"
                android:paddingTop="8dp"
                android:paddingRight="12dp"
                android:text="This is a Message"
                android:textColor="#ffffff"
                android:textSize="16sp"/>

        <TextView
                android:id="@+id/failed_mark"
                android:layout_width="40dp"
                android:layout_height="match_parent"
                android:paddingRight="5dp"
                android:src="@drawable/ic_launcher_background"
                android:text="!"
                android:textAlignment="viewEnd"
                android:textColor="@color/white"
                android:textSize="25dp"
                android:visibility="gone"/>
    </LinearLayout>

</LinearLayout>
```

### message à gauche de l'interface utilisateur de chat
<a name="create-ui-left-chat-message"></a>

**XML** :

```
// ./app/src/main/res/layout/card_view_left.xml
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_marginStart="8dp"
              android:layout_marginBottom="12dp"
              android:orientation="vertical">

    <TextView
            android:id="@+id/username_edit_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="UserName"/>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <androidx.cardview.widget.CardView
                android:id="@+id/card_message_other"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="left"
                android:layout_marginBottom="4dp"
                android:foreground="?android:attr/selectableItemBackground"
                app:cardBackgroundColor="@color/light_gray_2"
                app:cardCornerRadius="10dp"
                app:cardElevation="0dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent">

            <include layout="@layout/common_cell"/>
        </androidx.cardview.widget.CardView>

        <TextView
                android:id="@+id/dateText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginBottom="4dp"
                android:text="10:00"
                app:layout_constraintBottom_toBottomOf="@+id/card_message_other"
                app:layout_constraintLeft_toRightOf="@+id/card_message_other"/>
    </androidx.constraintlayout.widget.ConstraintLayout>


</LinearLayout>
```

### message à droite de l'interface utilisateur de chat
<a name="create-ui-right-chat-message"></a>

**XML** :

```
// ./app/src/main/res/layout/card_view_right.xml
 
<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"                                                   xmlns:app="http://schemas.android.com/apk/res-auto"                                                   android:layout_width="match_parent"                                                   android:layout_height="wrap_content" 
android:layout_marginEnd="8dp">

    <androidx.cardview.widget.CardView
            android:id="@+id/card_message_me"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginBottom="10dp"
            android:foreground="?android:attr/selectableItemBackground"
            app:cardBackgroundColor="@color/purple_500"
            app:cardCornerRadius="10dp"
            app:cardElevation="0dp"
            app:cardPreventCornerOverlap="false"
            app:cardUseCompatPadding="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent">

        <include layout="@layout/common_cell"/>

    </androidx.cardview.widget.CardView>

    <TextView
            android:id="@+id/dateText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="4dp"
            android:text="10:00"
            app:layout_constraintBottom_toBottomOf="@+id/card_message_me"
            app:layout_constraintRight_toLeftOf="@+id/card_message_me"/>

</androidx.constraintlayout.widget.ConstraintLayout>
```

### valeurs de couleur supplémentaires de l'interface utilisateur
<a name="create-ui-color-values"></a>

**XML** :

```
// ./app/src/main/res/values/colors.xml
 
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--    ...-->
    <color name="dark_gray">#4F4F4F</color>
    <color name="blue">#186ED3</color>
    <color name="dark_red">#b30000</color>
    <color name="light_gray">#B7B7B7</color>
    <color name="light_gray_2">#eef1f6</color>
</resources>
```

## appliquer la liaison d'affichage
<a name="chat-android-messages-events-view-binding"></a>

Nous tirons parti de la fonctionnalité Android [Liaison d'affichage](https://developer.android.com/topic/libraries/view-binding) pour pouvoir référencer des classes de liaison pour notre mise en page XML. Pour activer la fonctionnalité, définissez l'option de génération `viewBinding` sur `true` dans `./app/build.gradle` :

**Script Kotlin** :

```
 // ./app/build.gradle

android {
//    ...

    buildFeatures {
        viewBinding = true
    }
//    ...
}
```

Il est maintenant temps de connecter l'interface utilisateur à notre code Kotlin :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt
package com.chatterbox.myapp
// ...
const val TAG = "Chatterbox-MyApp"


class MainActivity : AppCompatActivity() {
//    ...

    private fun sendMessage(request: SendMessageRequest) {
        try {
            room?.sendMessage(
                request,
                object : SendMessageCallback {
                    override fun onRejected(request: SendMessageRequest, error: ChatError) {
                        runOnUiThread {
                            entries.addFailedRequest(request)
                            scrollToBottom()
                            Log.e(TAG, "Message rejected: ${error.errorMessage}")
                        }
                    }
                }
            )

            entries.addPendingRequest(request)

            binding.messageEditText.text.clear()
            scrollToBottom()
        } catch (error: Exception) {
            Log.e(TAG, error.message ?: "Unknown error occurred")
        }
    }

    private fun scrollToBottom() {
        binding.recyclerView.smoothScrollToPosition(entries.size - 1)
    }

    private fun sendButtonClick(view: View) {
        val content = binding.messageEditText.text.toString()
        if (content.trim().isEmpty()) {
            return
        }

        val request = SendMessageRequest(content)
        sendMessage(request)
    }
}
```

Nous ajoutons également des méthodes pour supprimer des messages et déconnecter les utilisateurs du chat, qui peuvent être invoquées à l'aide du menu contextuel des messages de chat :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
//    ...

class MainActivity : AppCompatActivity() {
//    ...

    private fun deleteMessage(request: DeleteMessageRequest) {
        room?.deleteMessage(
            request,
            object : DeleteMessageCallback {
                override fun onRejected(request: DeleteMessageRequest, error: ChatError) {
                    runOnUiThread {
                        Log.d(TAG, "Delete message rejected: ${error.errorMessage}")
                    }
                }
            }
        )
    }

    private fun disconnectUser(request: DisconnectUserRequest) {
        room?.disconnectUser(
            request,
            object : DisconnectUserCallback {
                override fun onRejected(request: DisconnectUserRequest, error: ChatError) {
                    runOnUiThread {
                        Log.d(TAG, "Disconnect user rejected: ${error.errorMessage}")
                    }
                }
            }
        )
    }
}
```

## Gérer les demandes de messages de chat
<a name="chat-android-messages-events-requests"></a>

Nous avons besoin d'un moyen de gérer nos demandes de messages de chat dans tous leurs états possibles :
+ En attente : un message a été envoyé à une salle de chat mais n'a pas encore été confirmé ou rejeté.
+ Confirmé : un message a été envoyé par la salle de chat à tous les utilisateurs (y compris à nous).
+ Rejeté : un message a été rejeté par la salle de chat avec un objet d'erreur.

Nous conserverons les demandes de chat et les messages de chat non résolus dans une [liste](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/mutable-list-of.html). La liste mérite une classe distincte, que nous appelons   `ChatEntries.kt`:

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/ChatEntries.kt

package com.chatterbox.myapp

import com.amazonaws.ivs.chat.messaging.entities.ChatMessage
import com.amazonaws.ivs.chat.messaging.requests.SendMessageRequest

sealed class ChatEntry() {
    class Message(val message: ChatMessage) : ChatEntry()
    class PendingRequest(val request: SendMessageRequest) : ChatEntry()
    class FailedRequest(val request: SendMessageRequest) : ChatEntry()
}

class ChatEntries {
    /* This list is kept in sorted order. ChatMessages are sorted by date, while pending and failed requests are kept in their original insertion point. */
    val entries = mutableListOf<ChatEntry>()
    var adapter: ChatListAdapter? = null

    val size get() = entries.size

    /**
     * Insert pending request at the end.
     */
    fun addPendingRequest(request: SendMessageRequest) {
        val insertIndex = entries.size
        entries.add(insertIndex, ChatEntry.PendingRequest(request))
        adapter?.notifyItemInserted(insertIndex)
    }

    /**
     * Insert received message at proper place based on sendTime. This can cause removal of pending requests.
     */
    fun addReceivedMessage(message: ChatMessage) {
        /* Skip if we have already handled that message. */
        val existingIndex = entries.indexOfLast { it is ChatEntry.Message && it.message.id == message.id }
        if (existingIndex != -1) {
            return
        }

        val removeIndex = entries.indexOfLast {
            it is ChatEntry.PendingRequest && it.request.requestId == message.requestId
        }
        if (removeIndex != -1) {
            entries.removeAt(removeIndex)
        }

        val insertIndexRaw = entries.indexOfFirst { it is ChatEntry.Message && it.message.sendTime > message.sendTime }
        val insertIndex = if (insertIndexRaw == -1) entries.size else insertIndexRaw
        entries.add(insertIndex, ChatEntry.Message(message))

        if (removeIndex == -1) {
            adapter?.notifyItemInserted(insertIndex)
        } else if (removeIndex == insertIndex) {
            adapter?.notifyItemChanged(insertIndex)
        } else {
            adapter?.notifyItemRemoved(removeIndex)
            adapter?.notifyItemInserted(insertIndex)
        }
    }

    fun addFailedRequest(request: SendMessageRequest) {
        val removeIndex = entries.indexOfLast {
            it is ChatEntry.PendingRequest && it.request.requestId == request.requestId
        }
        if (removeIndex != -1) {
            entries.removeAt(removeIndex)
            entries.add(removeIndex, ChatEntry.FailedRequest(request))
            adapter?.notifyItemChanged(removeIndex)
        } else {
            val insertIndex = entries.size
            entries.add(insertIndex, ChatEntry.FailedRequest(request))
            adapter?.notifyItemInserted(insertIndex)
        }
    }

    fun removeMessage(messageId: String) {
        val removeIndex = entries.indexOfFirst { it is ChatEntry.Message && it.message.id == messageId }
        entries.removeAt(removeIndex)
        adapter?.notifyItemRemoved(removeIndex)
    }

    fun removeFailedRequest(requestId: String) {
        val removeIndex = entries.indexOfFirst { it is ChatEntry.FailedRequest && it.request.requestId == requestId }
        entries.removeAt(removeIndex)
        adapter?.notifyItemRemoved(removeIndex)
    }

    fun removeAll() {
        entries.clear()
    }
}
```

Pour connecter notre liste à l'interface utilisateur, nous utilisons un [adaptateur](https://developer.android.com/reference/android/widget/Adapter). Pour plus d'informations, consultez [Liaison à des données avec AdapterView](https://developer.android.com/develop/ui/views/layout/binding) et [Classes de liaison générées](https://developer.android.com/topic/libraries/data-binding/generated-binding).

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/ChatListAdapter.kt

package com.chatterbox.myapp

import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import androidx.recyclerview.widget.RecyclerView
import com.amazonaws.ivs.chat.messaging.requests.DisconnectUserRequest
import java.text.DateFormat


class ChatListAdapter(
    private val entries: ChatEntries,
    private val onDisconnectUser: (request: DisconnectUserRequest) -> Unit,
) :
    RecyclerView.Adapter<ChatListAdapter.ViewHolder>() {
    var context: Context? = null
    var userId: String? = null

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val container: LinearLayout = view.findViewById(R.id.layout_container)
        val textView: TextView = view.findViewById(R.id.card_message_me_text_view)
        val failedMark: TextView = view.findViewById(R.id.failed_mark)
        val userNameText: TextView? = view.findViewById(R.id.username_edit_text)
        val dateText: TextView? = view.findViewById(R.id.dateText)
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        if (viewType == 0) {
            val rightView = LayoutInflater.from(viewGroup.context).inflate(R.layout.card_view_right, viewGroup, false)
            return ViewHolder(rightView)
        }
        val leftView = LayoutInflater.from(viewGroup.context).inflate(R.layout.card_view_left, viewGroup, false)
        return ViewHolder(leftView)
    }

    override fun getItemViewType(position: Int): Int {
        // Int 0 indicates to my message while Int 1 to other message
        val chatMessage = entries.entries[position]
        return if (chatMessage is ChatEntry.Message && chatMessage.message.sender.userId != userId) 1 else 0
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        return when (val entry = entries.entries[position]) {
            is ChatEntry.Message -> {
                viewHolder.textView.text = entry.message.content

                val bgColor = if (entry.message.sender.userId == userId) {
                    R.color.purple_500
                } else {
                    R.color.light_gray_2
                }
                viewHolder.container.setBackgroundColor(ContextCompat.getColor(context!!, bgColor))

                if (entry.message.sender.userId != userId) {
                    viewHolder.textView.setTextColor(Color.parseColor("#000000"))
                }

                viewHolder.failedMark.isGone = true

                viewHolder.itemView.setOnCreateContextMenuListener { menu, _, _ ->
                    menu.add("Kick out").setOnMenuItemClickListener {
                        val request = DisconnectUserRequest(entry.message.sender.userId, "Some reason")
                        onDisconnectUser(request)
                        true
                    }
                }

                viewHolder.userNameText?.text = entry.message.sender.userId
                viewHolder.dateText?.text =
                    DateFormat.getTimeInstance(DateFormat.SHORT).format(entry.message.sendTime)
            }

            is ChatEntry.PendingRequest -> {
                viewHolder.container.setBackgroundColor(ContextCompat.getColor(context!!, R.color.light_gray))
                viewHolder.textView.text = entry.request.content
                viewHolder.failedMark.isGone = true
                viewHolder.itemView.setOnCreateContextMenuListener(null)
                viewHolder.dateText?.text = "Sending"
            }

            is ChatEntry.FailedRequest -> {
                viewHolder.textView.text = entry.request.content
                viewHolder.container.setBackgroundColor(ContextCompat.getColor(context!!, R.color.dark_red))
                viewHolder.failedMark.isGone = false
                viewHolder.dateText?.text = "Failed"
            }
        }
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        context = recyclerView.context
    }

    override fun getItemCount() = entries.entries.size
}
```

## Étapes finales
<a name="chat-android-messages-events-final-steps"></a>

Il est temps de connecter notre nouvel adaptateur, en liant une classe `ChatEntries` à `MainActivity` :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

import com.chatterbox.myapp.databinding.ActivityMainBinding
import com.chatterbox.myapp.ChatListAdapter
import com.chatterbox.myapp.ChatEntries

class MainActivity : AppCompatActivity() {
    // ...
    private var entries = ChatEntries()
    private lateinit var adapter: ChatListAdapter
    private lateinit var binding: ActivityMainBinding

    /* see https://developer.android.com/topic/libraries/data-binding/generated-binding#create */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        /* Create room instance. */
        room = ChatRoom(REGION, ::fetchChatToken).apply {
            listener = roomListener
        }

        binding.sendButton.setOnClickListener(::sendButtonClick)
        binding.connectButton.setOnClickListener { connect() }

        setUpChatView()

        updateConnectionState(ConnectionState.DISCONNECTED)
    }

    private fun setUpChatView() {
        /* Setup Android Jetpack RecyclerView - see https://developer.android.com/develop/ui/views/layout/recyclerview.*/
        adapter = ChatListAdapter(entries, ::disconnectUser)
        entries.adapter = adapter

        val recyclerViewLayoutManager = LinearLayoutManager(this@MainActivity, LinearLayoutManager.VERTICAL, false)
        binding.recyclerView.layoutManager = recyclerViewLayoutManager
        binding.recyclerView.adapter = adapter

        binding.sendButton.setOnClickListener(::sendButtonClick)
        binding.messageEditText.setOnEditorActionListener { _, _, event ->
            val isEnterDown = (event.action == KeyEvent.ACTION_DOWN) && (event.keyCode == KeyEvent.KEYCODE_ENTER)
            if (!isEnterDown) {
                return@setOnEditorActionListener false
            }

            sendButtonClick(binding.sendButton)
            return@setOnEditorActionListener true
        }
    }
}
```

Comme nous avons déjà une classe chargée de suivre nos demandes de chat (`ChatEntries`), nous sommes prêts à implémenter le code pour manipuler `entries` dans `roomListener`. Nous mettrons à jour `entries` et `connectionState`, en fonction de l'événement auquel nous répondons :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
    //...
    

    private fun sendMessage(request: SendMessageRequest) {
    //...

    }

    private fun scrollToBottom() {
        binding.recyclerView.smoothScrollToPosition(entries.size - 1)
    }

    private val roomListener = object : ChatRoomListener {
        override fun onConnecting(room: ChatRoom) {
            Log.d(TAG, "[${Thread.currentThread().name}] onConnecting")
            runOnUiThread {
                updateConnectionState(ConnectionState.LOADING)
            }
        }

        override fun onConnected(room: ChatRoom) {
            Log.d(TAG, "[${Thread.currentThread().name}] onConnected")
            runOnUiThread {
                updateConnectionState(ConnectionState.CONNECTED)
            }
        }

        override fun onDisconnected(room: ChatRoom, reason: DisconnectReason) {
            Log.d(TAG, "[${Thread.currentThread().name}] onDisconnected")
            runOnUiThread {
                updateConnectionState(ConnectionState.DISCONNECTED)
                entries.removeAll()
            }
        }

        override fun onMessageReceived(room: ChatRoom, message: ChatMessage) {
            Log.d(TAG, "[${Thread.currentThread().name}] onMessageReceived $message")
            runOnUiThread {
                entries.addReceivedMessage(message)
                scrollToBottom()
            }
        }

        override fun onEventReceived(room: ChatRoom, event: ChatEvent) {
            Log.d(TAG, "[${Thread.currentThread().name}] onEventReceived $event")
        }

        override fun onMessageDeleted(room: ChatRoom, event: DeleteMessageEvent) {
            Log.d(TAG, "[${Thread.currentThread().name}] onMessageDeleted $event")
        }

        override fun onUserDisconnected(room: ChatRoom, event: DisconnectUserEvent) {
            Log.d(TAG, "[${Thread.currentThread().name}] onUserDisconnected $event")
        }
    }
}
```

Vous devriez maintenant être en mesure d'exécuter votre application \$1 (Voir [Créer et exécuter votre application](https://developer.android.com/studio/run#basic-build-run).) N'oubliez pas de faire fonctionner votre serveur backend lorsque vous utilisez l'application. Vous pouvez le lancer depuis le terminal à la racine de notre projet à l'aide de cette commande : `./gradlew :auth-server:run` ou en exécutant la tâche Gradle `auth-server:run`directement depuis Android Studio.

# Kit SDK de messagerie client de chat IVS : coroutines Kotlin, partie 1 : salles de chat
<a name="chat-sdk-kotlin-tutorial-chat-rooms"></a>

Il s'agit de la première partie d'un didacticiel en deux volets. Vous apprendrez les bases de l’utilisation du SDK de messagerie du Chat Amazon IVS en créant une application Android entièrement fonctionnelle à l’aide du langage de programmation [Kotlin](https://kotlinlang.org/) et des [coroutines](https://kotlinlang.org/docs/coroutines-overview.html). Nous appelons l'application *Chatterbox*.

Avant de commencer le module, prenez quelques minutes pour vous familiariser avec les prérequis, les concepts clés des jetons de discussion et le serveur principal nécessaire à la création de salles de chat.

Ces didacticiels sont conçus pour les développeurs Android expérimentés qui découvrent le SDK de messagerie d'IVS Chat. Vous devrez être à l'aise avec le langage de programmation Kotlin et la création d'interfaces utilisateur sur la plateforme Android.

Cette première partie du didacticiel est divisée en plusieurs sections :

1. [Configurer un serveur d'authentification/d'autorisation local](#chat-kotlin-rooms-auth-server)

1. [Créer un projet Chatterbox](#chat-kotlin-rooms-chatterbox)

1. [Se connecter à une salle de chat et observer les mises à jour de la connexion](#chat-kotlin-rooms-connect)

1. [Créer un fournisseur de jetons](#chat-kotlin-rooms-token-provider)

1. [Étapes suivantes](#chat-kotlin-rooms-next-steps)

Pour une documentation complète sur le SDK, commencez par le [SDK de messagerie client de chat Amazon IVS](chat-sdk.md) (ici dans le *Guide de l'utilisateur Chat Amazon IVS*) et la [Messagerie client de chat : référence du SDK pour Android](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/latest/) (sur GitHub).

## Conditions préalables
<a name="chat-kotlin-rooms-prerequisites"></a>
+ Familiarisez-vous avec Kotlin et la création d'applications sur la plateforme Android. Si vous n'êtes pas familiarisé avec la création d'applications pour Android, découvrez les bases dans le guide [Créer votre première application](https://developer.android.com/codelabs/basic-android-kotlin-compose-first-app#0) pour les développeurs Android.
+ Lisez et assurez-vous d'avoir compris [Mise en route avec le chat Amazon IVS](getting-started-chat.md).
+ Créez un utilisateur AWS IAM avec les fonctionnalités `CreateChatToken` et `CreateRoom` définies dans une politique IAM existante. (Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md).)
+ Assurez-vous que les clés secrètes et d'accès de cet utilisateur sont stockées dans un fichier d'informations d'identification AWS. Pour obtenir des instructions, consultez le [Guide de l'utilisateur de l'interface de ligne de commande AWS](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) (en particulier les [paramètres de configuration et de fichier d'informations d'identification](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)).
+ Créez une salle de chat et enregistrez son ARN. Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md). (Si vous n'enregistrez pas l'ARN, vous pourrez le consulter ultérieurement à l'aide de la console ou de l'API Chat.)

## Configurer un serveur d'authentification/d'autorisation local
<a name="chat-kotlin-rooms-auth-server"></a>

Votre serveur backend est chargé à la fois de créer des salles de chat et de générer les jetons de chat nécessaires au SDK de chat IVS pour Android pour authentifier et autoriser vos clients à accéder à vos salles de chat.

Consultez la section [Créer un jeton de chat](getting-started-chat-auth.md) dans *Mise en route avec Chat Amazon IVS*. Comme le montre l'organigramme, votre code côté serveur est chargée de créer un jeton de chat. Cela signifie que votre application doit fournir ses propres moyens de générer un jeton de chat en demandant un jeton à votre application côté serveur.

Nous utilisons l'infrastructure [Ktor](https://ktor.io/) pour créer un serveur local en direct qui gère la création de jetons de chat à l'aide de votre environnement AWS local. 

À ce stade, nous nous attendons à ce que vos informations d'identification AWS soient correctement configurées. Pour savoir comment procéder, consultez [Configuration des informations d'identification AWS temporaires et de la région AWS pour le développement](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-credentials.html).

Créez un nouveau répertoire et appelez-le `chatterbox` et, à l'intérieur, un autre, appelé `auth-server`*.*

La structure de notre serveur sera la suivante :

```
- auth-server
  - src
    - main
      - kotlin
        - com
          - chatterbox
            - authserver
              - Application.kt
       - resources
         - application.conf
         - logback.xml
   - build.gradle.kts
```

*Remarque : vous pouvez directement copier/coller le code ici dans les fichiers référencés.*

Ensuite, nous ajoutons toutes les dépendances et tous les plugins nécessaires au fonctionnement de notre serveur d'authentification :

**Script Kotlin:**

```
// ./auth-server/build.gradle.kts

plugins {
   application
   kotlin("jvm")
   kotlin("plugin.serialization").version("1.7.10")
}

application {   
   mainClass.set("io.ktor.server.netty.EngineMain")
}

dependencies {
   implementation("software.amazon.awssdk:ivschat:2.18.1")
   implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20")

   implementation("io.ktor:ktor-server-core:2.1.3")
   implementation("io.ktor:ktor-server-netty:2.1.3")
   implementation("io.ktor:ktor-server-content-negotiation:2.1.3")
   implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.3")

   implementation("ch.qos.logback:logback-classic:1.4.4")
}
```

Nous devons maintenant configurer la fonctionnalité de journalisation pour le serveur d'authentification. (Pour plus d'informations, veuillez consulter [Configure logger](https://ktor.io/docs/logging.html#configure-logger).)

**XML:**

```
// ./auth-server/src/main/resources/logback.xml

<configuration>
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
   </appender>
   <root level="trace">
      <appender-ref ref="STDOUT"/>
   </root>
   <logger name="org.eclipse.jetty" level="INFO"/>
   <logger name="io.netty" level="INFO"/>
</configuration>
```

Le serveur [Ktor](https://ktor.io/docs/welcome.html) nécessite des paramètres de configuration, qu'il charge automatiquement à partir du fichier `application.*` du répertoire `resources`. Nous les ajoutons donc également. (Pour plus d'informations, consultez [Configuration in a file](https://ktor.io/docs/configurations.html#configuration-file).)

**HOCON:**

```
// ./auth-server/src/main/resources/application.conf

ktor {
   deployment {
      port = 3000
   }
   application {
      modules = [ com.chatterbox.authserver.ApplicationKt.main ]
   }
}
```

Enfin, implémentons notre serveur :

**Kotlin:**

```
// ./auth-server/src/main/kotlin/com/chatterbox/authserver/Application.kt

package com.chatterbox.authserver

import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import software.amazon.awssdk.services.ivschat.IvschatClient
import software.amazon.awssdk.services.ivschat.model.CreateChatTokenRequest

@Serializable
data class ChatTokenParams(var userId: String, var roomIdentifier: String)

@Serializable
data class ChatToken(
   val token: String,
   val sessionExpirationTime: String,
   val tokenExpirationTime: String,
)

fun Application.main() {
   install(ContentNegotiation) {
      json(Json)
   }

   routing {
      post("/create_chat_token") {
         val callParameters = call.receive<ChatTokenParams>()
         val request = CreateChatTokenRequest.builder().roomIdentifier(callParameters.roomIdentifier)
            .userId(callParameters.userId).build()
         val token = IvschatClient.create()
            .createChatToken(request)

         call.respond(
            ChatToken(
                token.token(),
                token.sessionExpirationTime().toString(),
                token.tokenExpirationTime().toString()
            )
         )
      }
   }
}
```

## Créer un projet Chatterbox
<a name="chat-kotlin-rooms-chatterbox"></a>

Pour créer un projet Android, installez et ouvrez [Android Studio](https://developer.android.com/studio).

Suivez les étapes répertoriées dans le guide officiel Android [Créer un projet](https://developer.android.com/studio/projects/create-project). 
+ Dans [Choisir le type de projet](https://developer.android.com/studio/projects/create-project), choisissez le modèle de projet **Empty Activity** (Activité vide) pour notre application Chatterbox.
+ Dans [Configurer votre projet](https://developer.android.com/studio/projects/create-project#configure), choisissez les valeurs suivantes pour les champs de configuration :
  + **Nom** : My App
  + **Nom de package** : com.chatterbox.myapp
  + **Emplacement d'enregistrement** : pointez sur le répertoire `chatterbox` créé à l'étape précédente
  + **Langage** : Kotlin
  + **Niveau d'API minimum** : API 21 : Android 5.0 (Lollipop)

Après avoir correctement spécifié tous les paramètres de configuration, la structure de nos fichiers dans le dossier `chatterbox` doit ressembler à ce qui suit :

```
- app
  - build.gradle
  ...
- gradle
- .gitignore
- build.gradle
- gradle.properties
- gradlew
- gradlew.bat
- local.properties
- settings.gradle
- auth-server
  - src
    - main
      - kotlin
        - com
          - chatterbox
            - authserver
              - Application.kt
       - resources
         - application.conf
         - logback.xml
   - build.gradle.kts
```

Maintenant que nous avons un projet Android fonctionnel, nous pouvons ajouter [com.amazonaws:ivs-chat-messaging](https://mvnrepository.com/artifact/com.amazonaws/ivs-chat-messaging) et [org.jetbrains.kotlinx:kotlinx-coroutines-core](https://github.com/Kotlin/kotlinx.coroutines) à nos dépendances `build.gradle`. (Pour plus d'informations sur la boîte à outils de génération [Gradle](https://gradle.org/), voir [Configurer votre build](https://developer.android.com/build).) 

**Remarque** : en haut de chaque extrait de code, il y a un chemin vers le fichier dans lequel vous devez apporter des modifications à votre projet. Le chemin est relatif par rapport à la racine du projet.

**Kotlin:**

```
// ./app/build.gradle

plugins {
// ...
}

android {
// ...
}

dependencies {
    implementation 'com.amazonaws:ivs-chat-messaging:1.1.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'

// ...
}
```

Une fois la nouvelle dépendance ajoutée, exécutez **Synchroniser le projet avec les fichiers Gradle** dans Android Studio pour synchroniser le projet avec la nouvelle dépendance. (Pour de plus amples informations, veuillez consulter la section [Ajouter des dépendances de build](https://developer.android.com/build/dependencies).)

Pour exécuter facilement notre serveur d'authentification (créé dans la section précédente) à partir de la racine du projet, nous l'incluons en tant que nouveau module dans `settings.gradle`. (Pour plus d'informations, voir [Structurer et construire un composant de logiciel avec Gradle](https://docs.gradle.org/current/userguide/multi_project_builds.html).)

**Script Kotlin:**

```
// ./settings.gradle

// ...

rootProject.name = "My App"
include ':app'
include ':auth-server'
```

À présent, comme `auth-server` est inclus dans le projet Android, vous pouvez exécuter le serveur d'authentification à l'aide de la commande suivante depuis la racine du projet :

**Shell:**

```
./gradlew :auth-server:run         
```

## Se connecter à une salle de chat et observer les mises à jour de la connexion
<a name="chat-kotlin-rooms-connect"></a>

Pour ouvrir une connexion à une salle de chat, nous utilisons le [rappel du cycle de vie de l'activité onCreate()](https://developer.android.com/guide/components/activities/activity-lifecycle), qui se déclenche lorsque l'activité est créée pour la première fois. Le [constructeur ChatRoom](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html) nous oblige à fournir `region` et `tokenProvider` pour lancer une connexion à une salle.

**Remarque** : la fonction `fetchChatToken` présentée dans l'extrait ci-dessous sera implémentée dans [la section suivante](#chat-kotlin-rooms-token-provider).

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

// AWS region of the room that was created in Getting Started with Amazon IVS Chat
const val REGION = "us-west-2"

class MainActivity : AppCompatActivity() {
    private var room: ChatRoom? = null
    // ...

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      // Create room instance
      room = ChatRoom(REGION, ::fetchChatToken)
   }

// ...
}
```

L'affichage et la réaction aux modifications de la connexion à une salle de chat est un élément essentiel pour la création d'une application de chat comme `chatterbox`. Avant de pouvoir commencer à interagir avec la salle, nous devons nous abonner aux événements relatifs à l'état de connexion de la salle de chat pour obtenir des mises à jour.

Dans le SDK Chat pour la coroutine, [https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html) attend de nous que nous gérions les événements du cycle de vie des salles dans [Flux](https://kotlinlang.org/docs/flow.html). Pour l'instant, les fonctions enregistreront uniquement les messages de confirmation, lorsqu'ils sont invoqués :

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

const val TAG = "Chatterbox-MyApp"

class MainActivity : AppCompatActivity() {
// ...

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...

        // Create room instance
        room = ChatRoom(REGION, ::fetchChatToken).apply {
            lifecycleScope.launch {
                stateChanges().collect { state ->
                    Log.d(TAG, "state change to $state")
                }
            }

            lifecycleScope.launch {
                receivedMessages().collect { message ->
                    Log.d(TAG, "messageReceived $message")
                }
            }

            lifecycleScope.launch {
                receivedEvents().collect { event ->
                    Log.d(TAG, "eventReceived $event")
                }
            }

            lifecycleScope.launch {
                deletedMessages().collect { event ->
                    Log.d(TAG, "messageDeleted $event")
                }
            }

            lifecycleScope.launch {
                disconnectedUsers().collect { event ->
                    Log.d(TAG, "userDisconnected $event")
                }
            }
        }
    }
}
```

Ensuite, nous devons fournir la possibilité de lire l'état de la connexion à la salle. Nous le conserverons dans la [propriété](https://kotlinlang.org/docs/properties.html) `MainActivity.kt` et l’initialiserons à l’état DÉCONNECTÉ par défaut pour les salles (voir `ChatRoom` `state` dans la [référence du SDK IVS Chat pour Android](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/latest/)). Pour pouvoir maintenir l'état local à jour, nous devons implémenter une fonction de mise à jour de l'état ; appelons-le   `updateConnectionState`:

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
   private var connectionState = ChatRoom.State.DISCONNECTED

// ...

   private fun updateConnectionState(state: ChatRoom.State) {
      connectionState = state

     when (state) {
          ChatRoom.State.CONNECTED -> {
              Log.d(TAG, "room connected")
          }
          ChatRoom.State.DISCONNECTED -> {
              Log.d(TAG, "room disconnected")
          }
          ChatRoom.State.CONNECTING -> {
              Log.d(TAG, "room connecting")
          }
      }
}
```

Ensuite, nous intégrons notre fonction de mise à jour de l'état à la propriété [Chatroom.Listener](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/listener.html) :

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
// ...

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...

        // Create room instance
        room = ChatRoom(REGION, ::fetchChatToken).apply {
            lifecycleScope.launch {
                stateChanges().collect { state ->
                    Log.d(TAG, "state change to $state")
                    updateConnectionState(state)

                }
            }

      // ...

      }
   }
}
```

Maintenant que nous sommes en mesure d'enregistrer, d'écouter et de réagir aux mises à jour d'état de [ChatRoom](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/1.0.0/-amazon%20-i-v-s%20-chat%20-messaging%20-s-d-k%20for%20-android/com.amazonaws.ivs.chat.messaging/-chat-room/index.html), il est temps d'initialiser une connexion :

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
// ...

   private fun connect() {
      try {
         room?.connect()
      } catch (ex: Exception) {
         Log.e(TAG, "Error while calling connect()", ex)
      }
   }

   // ...
}
```

## Créer un fournisseur de jetons
<a name="chat-kotlin-rooms-token-provider"></a>

Il est temps de créer une fonction chargée de créer et de gérer des jetons de chat dans notre application. Dans cet exemple, nous utilisons le [client HTTP Retrofit pour Android](https://square.github.io/retrofit/).

Avant de pouvoir envoyer du trafic réseau, nous devons configurer une configuration de sécurité réseau pour Android. Pour plus d'informations, consultez [Network security configuration](https://developer.android.com/privacy-and-security/security-config).) Nous commençons par ajouter des autorisations réseau au fichier [App Manifest](https://developer.android.com/guide/topics/manifest/manifest-intro). Notez la balise `user-permission` et l'attribut `networkSecurityConfig` ajoutés, qui indiqueront notre nouvelle configuration de sécurité réseau. *Dans le code ci-dessous, remplacez *`<version>`* par le numéro de version actuel du SDK de chat pour Android (par exemple, 1.1.0)*.

**XML:**

```
// ./app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.chatterbox.myapp">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:fullBackupContent="@xml/backup_rules"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
// ...

// ./app/build.gradle


dependencies {
   implementation("com.amazonaws:ivs-chat-messaging:<version>")
// ...

   implementation("com.squareup.retrofit2:retrofit:2.9.0")
   implementation("com.squareup.retrofit2:converter-gson:2.9.0")
}
```

Déclarez votre adresse IP locale, par exemple `10.0.2.2` et les domaines `localhost` comme fiables, pour commencer à échanger des messages avec notre backend :

**XML:**

```
// ./app/src/main/res/xml/network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">10.0.2.2</domain>
        <domain includeSubdomains="true">localhost</domain>
    </domain-config>
</network-security-config>
```

Ensuite, nous devons ajouter une nouvelle dépendance, ainsi que l'[ajout d'un convertisseur Gson](https://github.com/square/retrofit/tree/trunk/retrofit-converters/gson) pour analyser les réponses HTTP. *Dans le code ci-dessous, remplacez *`<version>`* par le numéro de version actuel du SDK de chat pour Android (par exemple, 1.1.0)*.

**Script Kotlin:**

```
// ./app/build.gradle

dependencies {
   implementation("com.amazonaws:ivs-chat-messaging:<version>")
// ...

   implementation("com.squareup.retrofit2:retrofit:2.9.0")
   implementation("com.squareup.retrofit2:converter-gson:2.9.0")
}
```

Pour récupérer un jeton de chat, nous devons effectuer une requête HTTP POST depuis notre application `chatterbox`. Nous définissons la demande dans une interface que Retrofit doit implémenter. (Voir la [documentation de Retrofit](https://square.github.io/retrofit/) (français non garanti). Familiarisez-vous également avec la spécification de l’opération [CreateChatToken](https://docs.aws.amazon.com/ivs/latest/ChatAPIReference/API_CreateChatToken.html#API_CreateChatToken_RequestBody).)

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/network/ApiService.kt

package com.chatterbox.myapp.network

import com.amazonaws.ivs.chat.messaging.ChatToken
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST

data class CreateTokenParams(var userId: String, var roomIdentifier: String)

interface ApiService {
   @POST("create_chat_token")
   fun createChatToken(@Body params: CreateTokenParams): Call<ChatToken>
}


// ./app/src/main/java/com/chatterbox/myapp/network/RetrofitFactory.kt

package com.chatterbox.myapp.network

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitFactory {
   private const val BASE_URL = "http://10.0.2.2:3000"

   fun makeRetrofitService(): ApiService {
       return Retrofit.Builder()
           .baseUrl(BASE_URL)
           .addConverterFactory(GsonConverterFactory.create())
           .build().create(ApiService::class.java)
   }
}
```

Maintenant que le réseau est configuré, il est temps d'ajouter une fonction chargée de créer et de gérer notre jeton de chat. Nous l'ajoutons à `MainActivity.kt`, qui a été automatiquement créé lors de la [génération](#chat-kotlin-rooms-chatterbox) du projet :

**Kotlin:**

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import com.amazonaws.ivs.chat.messaging.*
import com.amazonaws.ivs.chat.messaging.coroutines.*
import com.chatterbox.myapp.network.CreateTokenParams
import com.chatterbox.myapp.network.RetrofitFactory
import retrofit2.Call
import java.io.IOException
import retrofit2.Callback
import retrofit2.Response

// custom tag for logging purposes
const val TAG = "Chatterbox-MyApp"

// any ID to be associated with auth token
const val USER_ID = "test user id"
// ID of the room the app wants to access. Must be an ARN. See Amazon Resource Names(ARNs)
const val ROOM_ID = "arn:aws:..."
// AWS region of the room that was created in Getting Started with Amazon IVS Chat
const val REGION = "us-west-2"

class MainActivity : AppCompatActivity() {

   private val service = RetrofitFactory.makeRetrofitService()
   private var userId: String = USER_ID

// ...

   private fun fetchChatToken(callback: ChatTokenCallback) {
      val params = CreateTokenParams(userId, ROOM_ID)
      service.createChatToken(params).enqueue(object : Callback<ChatToken> {
         override fun onResponse(call: Call<ChatToken>, response: Response<ChatToken>) {
            val token = response.body()
            if (token == null) {
               Log.e(TAG, "Received empty token response")
               callback.onFailure(IOException("Empty token response"))
               return
            }

            Log.d(TAG, "Received token response $token")
            callback.onSuccess(token)
         }

         override fun onFailure(call: Call<ChatToken>, throwable: Throwable) {
            Log.e(TAG, "Failed to fetch token", throwable)
            callback.onFailure(throwable)
         }
      })
   }
}
```

## Étapes suivantes
<a name="chat-kotlin-rooms-next-steps"></a>

Maintenant que vous avez établi une connexion à la salle de chat, passez à la seconde partie de ce didacticiel Coroutines Kotlin, [Messages et événements](chat-sdk-kotlin-tutorial-messages-events.md).

# Kit SDK de messagerie client Chat IVS : didacticiel Coroutines Kotlin, partie 2 : messages et événements
<a name="chat-sdk-kotlin-tutorial-messages-events"></a>

Cette seconde et dernière partie du didacticiel est divisée en plusieurs sections :

1. [Création d'une interface utilisateur pour l'envoi de messages](#chat-kotlin-messages-events-ui)

   1. [Mise en page principale de l'interface utilisateur](#chat-kotlin-messages-events-ui-main)

   1. [cellule de texte abstraite de l'interface utilisateur pour afficher le texte de manière cohérente](#chat-kotlin-messages-events-consistent-text)

   1. [message à gauche de l'interface utilisateur de chat](#chat-kotlin-messages-events-ui-left)

   1. [message à droite de l'interface utilisateur](#chat-kotlin-messages-events-ui-right)

   1. [valeurs de couleur supplémentaires de l'interface utilisateur](#chat-kotlin-messages-events-additional-color)

1. [appliquer la liaison d'affichage](#chat-kotlin-messages-events-apply-view-binding)

1. [Gérer les demandes de messages de chat](#chat-kotlin-messages-events-chat-message)

1. [Étapes finales](#chat-kotlin-messages-events-final-steps)

Pour une documentation complète sur le SDK, commencez par le [SDK de messagerie client Chat Amazon IVS](chat-sdk.md) (ici dans le *Guide de l'utilisateur Chat Amazon IVS*) et la [Messagerie client de chat : référence du SDK pour Android](https://aws.github.io/amazon-ivs-chat-messaging-sdk-android/latest/) (sur GitHub).

## Prérequis
<a name="chat-kotlin-messages-events-prerequisite"></a>

Assurez-vous d'avoir terminé la première partie de ce didacticiel relative aux [Salles de chat](chat-sdk-kotlin-tutorial-chat-rooms.md).

## Création d'une interface utilisateur pour l'envoi de messages
<a name="chat-kotlin-messages-events-ui"></a>

Maintenant que nous avons initialisé avec succès la connexion à la salle de chat, il est temps d'envoyer notre premier message. Pour cette fonctionnalité, une interface utilisateur est nécessaire. Nous ajouterons :
+ Bouton `connect`/`disconnect`
+ une saisie de message avec un bouton `send`
+ une liste de messages dynamiques. Pour la créer, nous utilisons Android Jetpack [RecyclerView](https://developer.android.com/develop/ui/views/layout/recyclerview).

### Mise en page principale de l'interface utilisateur
<a name="chat-kotlin-messages-events-ui-main"></a>

Consultez les [mises en page](https://developer.android.com/develop/ui/views/layout/declaring-layout) Android Jetpack dans la documentation pour les développeurs Android.

**XML** :

```
// ./app/src/main/res/layout/activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                     xmlns:app="http://schemas.android.com/apk/res-auto"
                                                     xmlns:tools="http://schemas.android.com/tools"
                                                     android:layout_width="match_parent"
                                                     android:layout_height="match_parent">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:app="http://schemas.android.com/apk/res-auto"
                  android:id="@+id/connect_view"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:gravity="center"
                  android:orientation="vertical">

        <androidx.cardview.widget.CardView
                android:id="@+id/connect_button"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity=""
                android:layout_marginStart="16dp"
                android:layout_marginTop="4dp"
                android:layout_marginEnd="16dp"
                android:clickable="true"
                android:elevation="16dp"
                android:focusable="true"
                android:foreground="?android:attr/selectableItemBackground"
                app:cardBackgroundColor="@color/purple_500"
                app:cardCornerRadius="10dp">

            <TextView
                    android:id="@+id/connect_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentEnd="true"
                    android:layout_gravity="center"
                    android:layout_weight="1"
                    android:paddingHorizontal="12dp"
                    android:text="Connect"
                    android:textColor="@color/white"
                    android:textSize="16sp"/>

            <ProgressBar
                    android:id="@+id/activity_indicator"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:layout_gravity="center"
                    android:layout_marginHorizontal="20dp"
                    android:indeterminateOnly="true"
                    android:indeterminateTint="@color/white"
                    android:indeterminateTintMode="src_atop"
                    android:keepScreenOn="true"
                    android:visibility="gone"/>
        </androidx.cardview.widget.CardView>

    </LinearLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/chat_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            android:visibility="visible"
            tools:context=".MainActivity">

        <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintBottom_toTopOf="@+id/layout_message_input"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent">

            <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recycler_view"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clipToPadding="false"
                    android:paddingTop="70dp"
                    android:paddingBottom="20dp"/>
        </RelativeLayout>

        <RelativeLayout
                android:id="@+id/layout_message_input"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:clipToPadding="false"
                android:drawableTop="@android:color/black"
                android:elevation="18dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent">

            <EditText
                    android:id="@+id/message_edit_text"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_marginStart="16dp"
                    android:layout_toStartOf="@+id/send_button"
                    android:background="@android:color/transparent"
                    android:hint="Enter Message"
                    android:inputType="text"
                    android:maxLines="6"
                    tools:ignore="Autofill"/>

            <Button
                    android:id="@+id/send_button"
                    android:layout_width="84dp"
                    android:layout_height="48dp"
                    android:layout_alignParentEnd="true"
                    android:background="@color/black"
                    android:foreground="?android:attr/selectableItemBackground"
                    android:text="Send"
                    android:textColor="@color/white"
                    android:textSize="12dp"/>
        </RelativeLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.coordinatorlayout.widget.CoordinatorLayout>
```

### cellule de texte abstraite de l'interface utilisateur pour afficher le texte de manière cohérente
<a name="chat-kotlin-messages-events-consistent-text"></a>

**XML** :

```
// ./app/src/main/res/layout/common_cell.xml
   
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/layout_container"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:background="@color/light_gray"
              android:minWidth="100dp"
              android:orientation="vertical">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">

        <TextView
                android:id="@+id/card_message_me_text_view"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_marginBottom="8dp"
                android:maxWidth="260dp"
                android:paddingLeft="12dp"
                android:paddingTop="8dp"
                android:paddingRight="12dp"
                android:text="This is a Message"
                android:textColor="#ffffff"
                android:textSize="16sp"/>

        <TextView
                android:id="@+id/failed_mark"
                android:layout_width="40dp"
                android:layout_height="match_parent"
                android:paddingRight="5dp"
                android:src="@drawable/ic_launcher_background"
                android:text="!"
                android:textAlignment="viewEnd"
                android:textColor="@color/white"
                android:textSize="25dp"
                android:visibility="gone"/>
    </LinearLayout>

</LinearLayout>
```

### message à gauche de l'interface utilisateur de chat
<a name="chat-kotlin-messages-events-ui-left"></a>

**XML:**

```
// ./app/src/main/res/layout/card_view_left.xml
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_marginStart="8dp"
              android:layout_marginBottom="12dp"
              android:orientation="vertical">

    <TextView
            android:id="@+id/username_edit_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="UserName"/>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <androidx.cardview.widget.CardView
                android:id="@+id/card_message_other"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="left"
                android:layout_marginBottom="4dp"
                android:foreground="?android:attr/selectableItemBackground"
                app:cardBackgroundColor="@color/light_gray_2"
                app:cardCornerRadius="10dp"
                app:cardElevation="0dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent">

            <include layout="@layout/common_cell"/>
        </androidx.cardview.widget.CardView>

        <TextView
                android:id="@+id/dateText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginBottom="4dp"
                android:text="10:00"
                app:layout_constraintBottom_toBottomOf="@+id/card_message_other"
                app:layout_constraintLeft_toRightOf="@+id/card_message_other"/>
    </androidx.constraintlayout.widget.ConstraintLayout>


</LinearLayout>
```

### message à droite de l'interface utilisateur
<a name="chat-kotlin-messages-events-ui-right"></a>

**XML:**

```
// ./app/src/main/res/layout/card_view_right.xml
 
<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"                                                   xmlns:app="http://schemas.android.com/apk/res-auto"                                                   android:layout_width="match_parent"                                                   android:layout_height="wrap_content" 
android:layout_marginEnd="8dp">

    <androidx.cardview.widget.CardView
            android:id="@+id/card_message_me"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginBottom="10dp"
            android:foreground="?android:attr/selectableItemBackground"
            app:cardBackgroundColor="@color/purple_500"
            app:cardCornerRadius="10dp"
            app:cardElevation="0dp"
            app:cardPreventCornerOverlap="false"
            app:cardUseCompatPadding="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent">

        <include layout="@layout/common_cell"/>

    </androidx.cardview.widget.CardView>

    <TextView
            android:id="@+id/dateText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="4dp"
            android:text="10:00"
            app:layout_constraintBottom_toBottomOf="@+id/card_message_me"
            app:layout_constraintRight_toLeftOf="@+id/card_message_me"/>

</androidx.constraintlayout.widget.ConstraintLayout>
```

### valeurs de couleur supplémentaires de l'interface utilisateur
<a name="chat-kotlin-messages-events-additional-color"></a>

**XML** :

```
// ./app/src/main/res/values/colors.xml
 
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--    ...-->
    <color name="dark_gray">#4F4F4F</color>
    <color name="blue">#186ED3</color>
    <color name="dark_red">#b30000</color>
    <color name="light_gray">#B7B7B7</color>
    <color name="light_gray_2">#eef1f6</color>
</resources>
```

## appliquer la liaison d'affichage
<a name="chat-kotlin-messages-events-apply-view-binding"></a>

Nous tirons parti de la fonctionnalité Android [Liaison d'affichage](https://developer.android.com/topic/libraries/view-binding) pour pouvoir référencer des classes de liaison pour notre mise en page XML. Pour activer la fonctionnalité, définissez l'option de génération `viewBinding` sur `true` dans `./app/build.gradle` :

**Script Kotlin** :

```
 // ./app/build.gradle

android {
//    ...

    buildFeatures {
        viewBinding = true
    }
//    ...
}
```

Il est maintenant temps de connecter l'interface utilisateur à notre code Kotlin :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
    // ...
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Create room instance
        room = ChatRoom(REGION, ::fetchChatToken).apply {
            // ...
        }

        binding.sendButton.setOnClickListener(::sendButtonClick)
        binding.connectButton.setOnClickListener {connect()}

        setUpChatView()

        updateConnectionState(ChatRoom.State.DISCONNECTED)
    }

    private fun sendMessage(request: SendMessageRequest) {
        lifecycleScope.launch {
           try {
               binding.messageEditText.text.clear()
               room?.awaitSendMessage(request)
           } catch (exception: ChatException) {
               Log.e(TAG, "Message rejected: ${exception.message}")
           } catch (exception: Exception) {
               Log.e(TAG, exception.message ?: "Unknown error occurred")
           }
        }
    }

    private fun sendButtonClick(view: View) {
        val content = binding.messageEditText.text.toString()
        if (content.trim().isEmpty()) {
            return
        }

        val request = SendMessageRequest(content)
        sendMessage(request)
    }
// ...

}
```

Nous ajoutons également des méthodes pour supprimer des messages et déconnecter les utilisateurs du chat, qui peuvent être invoquées à l'aide du menu contextuel des messages de chat :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
//    ...

class MainActivity : AppCompatActivity() {
//    ...

    private fun deleteMessage(request: DeleteMessageRequest) {
        lifecycleScope.launch {
           try {
               room?.awaitDeleteMessage(request)
           } catch (exception: ChatException) {
               Log.e(TAG, "Delete message rejected: ${exception.message}")
           } catch (exception: Exception) {
               Log.e(TAG, exception.message ?: "Unknown error occurred")
           }
        }
    }

    private fun disconnectUser(request: DisconnectUserRequest) {
        lifecycleScope.launch {
           try {
               room?.awaitDisconnectUser(request)
           } catch (exception: ChatException) {
               Log.e(TAG, "Disconnect user rejected: ${exception.message}")
           } catch (exception: Exception) {
               Log.e(TAG, exception.message ?: "Unknown error occurred")
           }
        }
    }
}
```

## Gérer les demandes de messages de chat
<a name="chat-kotlin-messages-events-chat-message"></a>

Nous avons besoin d'un moyen de gérer nos demandes de messages de chat dans tous leurs états possibles :
+ En attente : un message a été envoyé à une salle de chat mais n'a pas encore été confirmé ou rejeté.
+ Confirmé : un message a été envoyé par la salle de chat à tous les utilisateurs (y compris à nous).
+ Rejeté : un message a été rejeté par la salle de chat avec un objet d'erreur.

Nous conserverons les demandes de chat et les messages de chat non résolus dans une [liste](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/mutable-list-of.html). La liste mérite une classe distincte, que nous appelons   `ChatEntries.kt`:

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/ChatEntries.kt

package com.chatterbox.myapp

import com.amazonaws.ivs.chat.messaging.entities.ChatMessage
import com.amazonaws.ivs.chat.messaging.requests.SendMessageRequest

sealed class ChatEntry() {
    class Message(val message: ChatMessage) : ChatEntry()
    class PendingRequest(val request: SendMessageRequest) : ChatEntry()
    class FailedRequest(val request: SendMessageRequest) : ChatEntry()
}

class ChatEntries {
    /* This list is kept in sorted order. ChatMessages are sorted by date, while pending and failed requests are kept in their original insertion point. */
    val entries = mutableListOf<ChatEntry>()
    var adapter: ChatListAdapter? = null

    val size get() = entries.size

    /**
     * Insert pending request at the end.
     */
    fun addPendingRequest(request: SendMessageRequest) {
        val insertIndex = entries.size
        entries.add(insertIndex, ChatEntry.PendingRequest(request))
        adapter?.notifyItemInserted(insertIndex)
    }

    /**
     * Insert received message at proper place based on sendTime. This can cause removal of pending requests.
     */
    fun addReceivedMessage(message: ChatMessage) {
        /* Skip if we have already handled that message. */
        val existingIndex = entries.indexOfLast { it is ChatEntry.Message && it.message.id == message.id }
        if (existingIndex != -1) {
            return
        }

        val removeIndex = entries.indexOfLast {
            it is ChatEntry.PendingRequest && it.request.requestId == message.requestId
        }
        if (removeIndex != -1) {
            entries.removeAt(removeIndex)
        }

        val insertIndexRaw = entries.indexOfFirst { it is ChatEntry.Message && it.message.sendTime > message.sendTime }
        val insertIndex = if (insertIndexRaw == -1) entries.size else insertIndexRaw
        entries.add(insertIndex, ChatEntry.Message(message))

        if (removeIndex == -1) {
            adapter?.notifyItemInserted(insertIndex)
        } else if (removeIndex == insertIndex) {
            adapter?.notifyItemChanged(insertIndex)
        } else {
            adapter?.notifyItemRemoved(removeIndex)
            adapter?.notifyItemInserted(insertIndex)
        }
    }

    fun addFailedRequest(request: SendMessageRequest) {
        val removeIndex = entries.indexOfLast {
            it is ChatEntry.PendingRequest && it.request.requestId == request.requestId
        }
        if (removeIndex != -1) {
            entries.removeAt(removeIndex)
            entries.add(removeIndex, ChatEntry.FailedRequest(request))
            adapter?.notifyItemChanged(removeIndex)
        } else {
            val insertIndex = entries.size
            entries.add(insertIndex, ChatEntry.FailedRequest(request))
            adapter?.notifyItemInserted(insertIndex)
        }
    }

    fun removeMessage(messageId: String) {
        val removeIndex = entries.indexOfFirst { it is ChatEntry.Message && it.message.id == messageId }
        entries.removeAt(removeIndex)
        adapter?.notifyItemRemoved(removeIndex)
    }

    fun removeFailedRequest(requestId: String) {
        val removeIndex = entries.indexOfFirst { it is ChatEntry.FailedRequest && it.request.requestId == requestId }
        entries.removeAt(removeIndex)
        adapter?.notifyItemRemoved(removeIndex)
    }

    fun removeAll() {
        entries.clear()
    }
}
```

Pour connecter notre liste à l'interface utilisateur, nous utilisons un [adaptateur](https://developer.android.com/reference/android/widget/Adapter). Pour plus d'informations, consultez [Liaison à des données avec AdapterView](https://developer.android.com/develop/ui/views/layout/binding) et [Classes de liaison générées](https://developer.android.com/topic/libraries/data-binding/generated-binding).

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/ChatListAdapter.kt

package com.chatterbox.myapp

import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import androidx.recyclerview.widget.RecyclerView
import com.amazonaws.ivs.chat.messaging.requests.DisconnectUserRequest
import java.text.DateFormat


class ChatListAdapter(
    private val entries: ChatEntries,
    private val onDisconnectUser: (request: DisconnectUserRequest) -> Unit,
) :
    RecyclerView.Adapter<ChatListAdapter.ViewHolder>() {
    var context: Context? = null
    var userId: String? = null

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val container: LinearLayout = view.findViewById(R.id.layout_container)
        val textView: TextView = view.findViewById(R.id.card_message_me_text_view)
        val failedMark: TextView = view.findViewById(R.id.failed_mark)
        val userNameText: TextView? = view.findViewById(R.id.username_edit_text)
        val dateText: TextView? = view.findViewById(R.id.dateText)
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        if (viewType == 0) {
            val rightView = LayoutInflater.from(viewGroup.context).inflate(R.layout.card_view_right, viewGroup, false)
            return ViewHolder(rightView)
        }
        val leftView = LayoutInflater.from(viewGroup.context).inflate(R.layout.card_view_left, viewGroup, false)
        return ViewHolder(leftView)
    }

    override fun getItemViewType(position: Int): Int {
        // Int 0 indicates to my message while Int 1 to other message
        val chatMessage = entries.entries[position]
        return if (chatMessage is ChatEntry.Message && chatMessage.message.sender.userId != userId) 1 else 0
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        return when (val entry = entries.entries[position]) {
            is ChatEntry.Message -> {
                viewHolder.textView.text = entry.message.content

                val bgColor = if (entry.message.sender.userId == userId) {
                    R.color.purple_500
                } else {
                    R.color.light_gray_2
                }
                viewHolder.container.setBackgroundColor(ContextCompat.getColor(context!!, bgColor))

                if (entry.message.sender.userId != userId) {
                    viewHolder.textView.setTextColor(Color.parseColor("#000000"))
                }

                viewHolder.failedMark.isGone = true

                viewHolder.itemView.setOnCreateContextMenuListener { menu, _, _ ->
                    menu.add("Kick out").setOnMenuItemClickListener {
                        val request = DisconnectUserRequest(entry.message.sender.userId, "Some reason")
                        onDisconnectUser(request)
                        true
                    }
                }

                viewHolder.userNameText?.text = entry.message.sender.userId
                viewHolder.dateText?.text =
                    DateFormat.getTimeInstance(DateFormat.SHORT).format(entry.message.sendTime)
            }

            is ChatEntry.PendingRequest -> {
                viewHolder.container.setBackgroundColor(ContextCompat.getColor(context!!, R.color.light_gray))
                viewHolder.textView.text = entry.request.content
                viewHolder.failedMark.isGone = true
                viewHolder.itemView.setOnCreateContextMenuListener(null)
                viewHolder.dateText?.text = "Sending"
            }

            is ChatEntry.FailedRequest -> {
                viewHolder.textView.text = entry.request.content
                viewHolder.container.setBackgroundColor(ContextCompat.getColor(context!!, R.color.dark_red))
                viewHolder.failedMark.isGone = false
                viewHolder.dateText?.text = "Failed"
            }
        }
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        context = recyclerView.context
    }

    override fun getItemCount() = entries.entries.size
}
```

## Étapes finales
<a name="chat-kotlin-messages-events-final-steps"></a>

Il est temps de connecter notre nouvel adaptateur, en liant une classe `ChatEntries` à `MainActivity` :

**Kotlin**:

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

import com.chatterbox.myapp.databinding.ActivityMainBinding
import com.chatterbox.myapp.ChatListAdapter
import com.chatterbox.myapp.ChatEntries

class MainActivity : AppCompatActivity() {
    // ...
    private var entries = ChatEntries()
    private lateinit var adapter: ChatListAdapter

    // ...

    private fun setUpChatView() {
        adapter = ChatListAdapter(entries, ::disconnectUser)
        entries.adapter = adapter

        val recyclerViewLayoutManager = LinearLayoutManager(this@MainActivity, LinearLayoutManager.VERTICAL, false)
        binding.recyclerView.layoutManager = recyclerViewLayoutManager
        binding.recyclerView.adapter = adapter

        binding.sendButton.setOnClickListener(::sendButtonClick)
        binding.messageEditText.setOnEditorActionListener { _, _, event ->
            val isEnterDown = (event.action == KeyEvent.ACTION_DOWN) && (event.keyCode == KeyEvent.KEYCODE_ENTER)
            if (!isEnterDown) {
                return@setOnEditorActionListener false
            }

            sendButtonClick(binding.sendButton)
            return@setOnEditorActionListener true
        }
    }
}
```

Comme nous avons déjà une classe chargée de suivre nos demandes de chat (`ChatEntries`), nous sommes prêts à implémenter le code pour manipuler `entries` dans roomListener. Nous mettrons à jour `entries` et `connectionState`, en fonction de l'événement auquel nous répondons :

**Kotlin** :

```
// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt

package com.chatterbox.myapp
// ...

class MainActivity : AppCompatActivity() {
// ...


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Create room instance
        room = ChatRoom(REGION, ::fetchChatToken).apply {
            lifecycleScope.launch {
                stateChanges().collect { state ->
                    Log.d(TAG, "state change to $state")
                    updateConnectionState(state)
                    if (state == ChatRoom.State.DISCONNECTED) {
                       entries.removeAll()
                    }
                }
            }

            lifecycleScope.launch {
                receivedMessages().collect { message ->
                    Log.d(TAG, "messageReceived $message")
                    entries.addReceivedMessage(message)
                }
            }

            lifecycleScope.launch {
                receivedEvents().collect { event ->
                    Log.d(TAG, "eventReceived $event")
                }
            }

            lifecycleScope.launch {
                deletedMessages().collect { event ->
                    Log.d(TAG, "messageDeleted $event")
                    entries.removeMessage(event.messageId)
                }
            }

            lifecycleScope.launch {
                disconnectedUsers().collect { event ->
                    Log.d(TAG, "userDisconnected $event")
                }
            }
        }

        binding.sendButton.setOnClickListener(::sendButtonClick)
        binding.connectButton.setOnClickListener {connect()}

        setUpChatView()

        updateConnectionState(ChatRoom.State.DISCONNECTED)
    }

// ...

}
```

Vous devriez maintenant être en mesure d'exécuter votre application \$1 (Voir [Créer et exécuter votre application](https://developer.android.com/studio/run#basic-build-run).) N'oubliez pas de faire fonctionner votre serveur backend lorsque vous utilisez l'application. Vous pouvez le lancer depuis le terminal à la racine de notre projet à l'aide de cette commande : `./gradlew :auth-server:run` ou en exécutant la tâche Gradle `auth-server:run`directement depuis Android Studio.

# Kit SDK de messagerie client Chat IVS : guide iOS
<a name="chat-sdk-ios"></a>

Le kit SDK de messagerie client Amazon Interactive Video (IVS) Chat pour iOS fournit des interfaces qui vous permettent d'intégrer notre [API de messagerie IVS Chat](https://docs.aws.amazon.com//ivs/latest/chatmsgapireference/welcome.html) sur les plateformes utilisant le [langage de programmation Swift](https://developer.apple.com/swift/) d'Apple.

**Dernière version du kit SDK de messagerie client IVS Chat pour iOS :** 1.0.1 ([Notes de mise à jour](https://docs.aws.amazon.com//ivs/latest/ChatUserGuide/release-notes.html#aug08-25))

**Documentation de référence et didacticiels :** pour plus d'informations sur les méthodes les plus importantes disponibles dans le kit SDK de messagerie client Amazon IVS Chat pour iOS, veuillez consulter la documentation de référence à l'adresse : [https://aws.github.io/amazon-ivs-chat-messaging-sdk-ios/1.0.1/](https://aws.github.io/amazon-ivs-chat-messaging-sdk-ios/1.0.1/). Ce référentiel contient également divers articles et didacticiels.

**Exemple de code :** voir l’exemple de référentiel iOS sur GitHub : [https://github.com/aws-samples/amazon-ivs-chat-for-ios-demo](https://github.com/aws-samples/amazon-ivs-chat-for-ios-demo).

**Exigences de la plateforme :** iOS 13.0 ou version ultérieure est requis pour le développement.

# Démarrez avec le SDK de messagerie client Chat IVS iOS
<a name="chat-ios-getting-started"></a>

Nous vous recommandons d'intégrer le kit SDK via [Swift Package Manager](#chat-ios-install-sdk-swiftpm). Vous pouvez également [intégrer le cadre manuellement](#chat-ios-install-sdk-manual).

Après avoir intégré le kit SDK, vous pouvez l'importer en ajoutant le code suivant en haut de votre fichier Swift concerné :

```
import AmazonIVSChatMessaging
```

## Swift Package Manager
<a name="chat-ios-install-sdk-swiftpm"></a>

Pour utiliser la bibliothèque `AmazonIVSChatMessaging` dans un projet Swift Package Manager, ajoutez-la aux dépendances de votre package et aux dépendances de vos cibles pertinentes :

1. Téléchargez la dernière version du `.xcframework` depuis [https://ivschat.live-video.net/1.0.1/AmazonIVSChatMessaging.xcframework.zip](https://ivschat.live-video.net/1.0.1/AmazonIVSChatMessaging.xcframework.zip).

1. Dans votre terminal, exécutez :

   ```
   shasum -a 256 path/to/downloaded/AmazonIVSChatMessaging.xcframework.zip
   ```

1. Prenez la sortie de l'étape précédente et collez-la dans la propriété checksum de `.binaryTarget`, comme indiqué ci-dessous dans le fichier `Package.swift` de votre projet :

   ```
   let package = Package(
      // name, platforms, products, etc.
      dependencies: [
         // other dependencies
      ],
      targets: [
         .target(
            name: "<target-name>",
            dependencies: [
               // If you want to only bring in the SDK
               .binaryTarget(
                  name: "AmazonIVSChatMessaging",
                  url: "https://ivschat.live-video.net/1.0.1/AmazonIVSChatMessaging.xcframework.zip",
                  checksum: "<SHA-extracted-using-steps-detailed-above>"
               ),
               // your other dependencies
            ],
         ),
         // other targets
      ]
   )
   ```

## Installation manuelle
<a name="chat-ios-install-sdk-manual"></a>

1. Téléchargez la dernière version depuis [https://ivschat.live-video.net/1.0.1/AmazonIVSChatMessaging.xcframework.zip](https://ivschat.live-video.net/1.0.1/AmazonIVSChatMessaging.xcframework.zip).

1. Extrayez le contenu de l’archive. `AmazonIVSChatMessaging.xcframework` contient le kit SDK pour l’appareil et le simulateur.

1. Intégrez le `AmazonIVSChatMessaging.xcframework` extrait en le faisant glisser dans la section **Frameworks, Libraries, and Embedded Content** (Cadre, bibliothèques et contenu intégré) de l'onglet **General** (Général) de votre cible d'application :  
![\[La section Frameworks, Libraries, and Embedded Content (Cadre, bibliothèques et contenu intégré) de l’onglet General (Général) de votre cible d’application.\]](http://docs.aws.amazon.com/fr_fr/ivs/latest/ChatUserGuide/images/Chat_SDK_iOS_Manual_Installation.png)

# Utilisation de l'SDK de messagerie client Chat Amazon IVS iOS
<a name="chat-ios-using-sdk"></a>

Ce document explique les étapes nécessaires à l'utilisation de l'SDK de messagerie client Chat Amazon IVS iOS.

## Se connecter à une salle de chat
<a name="chat-ios-connect-room"></a>

Avant de commencer, vous devez être familiarisé avec la [Mise en route avec Amazon IVS Chat](getting-started-chat.md). Consultez également les exemples d'applications pour le [web](https://github.com/aws-samples/amazon-ivs-chat-web-demo), [Android](https://github.com/aws-samples/amazon-ivs-chat-for-android-demo) et [iOS](https://github.com/aws-samples/amazon-ivs-chat-for-ios-demo).

Pour se connecter à une salle de chat, votre application a besoin d'un moyen de récupérer un jeton de chat fourni par votre backend. Votre application récupérera probablement un jeton de chat à l'aide d'une demande réseau envoyée à votre backend.

Pour transférer ce jeton de chat récupéré au kit SDK, le modèle `ChatRoom` du kit SDK exige que vous fournissiez une fonction `async` ou l'instance d'un objet conforme au protocole `ChatTokenProvider` fourni au moment de l'initialisation. La valeur renvoyée par l'une de ces méthodes doit être une instance du modèle `ChatToken` du kit SDK.

**Remarque :** vous renseignez les instances du modèle `ChatToken` à l'aide des données récupérées depuis votre backend. Les champs requis pour initialiser une instance `ChatToken` sont les mêmes que les champs de la réponse [CreateChatToken](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_CreateChatToken.html). Pour plus d'informations sur l'initialisation des instances du modèle `ChatToken`, veuillez consulter la rubrique [Créer une instance de ChatToken](#chat-ios-create-chattoken). Souvenez-vous que *votre backend* est chargé de fournir des données dans la réponse `CreateChatToken` à votre application. La manière dont vous décidez de communiquer avec votre backend pour générer des jetons de chat dépend de votre application et de son infrastructure.

Après avoir choisi votre stratégie pour fournir un `ChatToken` au kit SDK, appelez `.connect()` après avoir initialisé une instance `ChatRoom` avec votre fournisseur de jetons et la *région AWS* que votre backend a utilisée pour créer la salle de chat à laquelle vous essayez de vous connecter. Veuillez noter que `.connect()` est une fonction asynchrone de lancement :

```
import AmazonIVSChatMessaging

let room = ChatRoom(
   awsRegion: <region-your-backend-created-the-chat-room-in>,
   tokenProvider: <your-chosen-token-provider-strategy>
)
try await room.connect()
```

### Se conformer au protocole ChatTokenProvider
<a name="chat-ios-chattokenprovider-protocol"></a>

Pour le paramètre `tokenProvider` dans l'initialiseur de `ChatRoom`, vous pouvez fournir une instance de `ChatTokenProvider`. Voici un exemple d'objet conforme à `ChatTokenProvider` :

```
import AmazonIVSChatMessaging

// This object should exist somewhere in your app
class ChatService: ChatTokenProvider {
   func getChatToken() async throws -> ChatToken {
      let request = YourApp.getTokenURLRequest
      let data = try await URLSession.shared.data(for: request).0
      ...
      return ChatToken(
         token: String(data: data, using: .utf8)!,
         tokenExpirationTime: ..., // this is optional
         sessionExpirationTime: ... // this is optional
      )
   }
}
```

Vous pouvez ensuite prendre une instance de cet objet conforme et la transmettre à l'initialiseur de `ChatRoom` :

```
// This should be the same AWS Region that you used to create
// your Chat Room in the Control Plane
let awsRegion = "us-west-2"
let service = ChatService()
let room = ChatRoom(
   awsRegion: awsRegion,
   tokenProvider: service
)
try await room.connect()
```

### Fournir une fonction asynchrone dans Swift
<a name="chat-ios-retrievechattoken-async-function"></a>

Supposons que vous disposiez déjà d'un gestionnaire que vous utilisez pour gérer les demandes réseau de votre application. Elle peut ressembler à ceci :

```
import AmazonIVSChatMessaging

class EndpointManager {
   func getAccounts() async -> AppUser {...}
   func signIn(user: AppUser) async {...}
   ...
}
```

Vous pouvez simplement ajouter une autre fonction dans votre gestionnaire afin de récupérer un `ChatToken` depuis votre backend :

```
import AmazonIVSChatMessaging

class EndpointManager {
   ...
   func retrieveChatToken() async -> ChatToken {...}
}
```

Ensuite, utilisez la référence à cette fonction dans Swift lors de l'initialisation d'une `ChatRoom` :

```
import AmazonIVSChatMessaging

let endpointManager: EndpointManager
let room = ChatRoom(
   awsRegion: endpointManager.awsRegion,
   tokenProvider: endpointManager.retrieveChatToken
)
try await room.connect()
```

## Créer une instance de ChatToken
<a name="chat-ios-create-chattoken"></a>

Vous pouvez facilement créer une instance de `ChatToken` à l'aide de l'initialiseur fourni dans le kit SDK. Veuillez consulter la documentation dans `Token.swift` pour en savoir plus sur les propriétés de `ChatToken`. 

```
import AmazonIVSChatMessaging

let chatToken = ChatToken(
   token: <token-string-retrieved-from-your-backend>,
   tokenExpirationTime: nil, // this is optional
   sessionExpirationTime: nil // this is optional
)
```

### Utiliser le protocole Decodable
<a name="chat-ios-create-chattoken-decodable"></a>

Si, lors de l'interfaçage avec l'API IVS Chat, votre backend décide de simplement transmettre la réponse [CreateChatToken](https://docs.aws.amazon.com/ivs/latest/ChatAPIReference/API_CreateChatToken.html) à votre application frontend, vous pouvez profiter de la conformité de `ChatToken` au protocole `Decodable` de Swift. Cependant, il y a un hic.

La charge utile de réponse `CreateChatToken` utilise des chaînes pour les dates formatées à l'aide de la [norme ISO 8601 pour les horodatages Internet](https://en.wikipedia.org/wiki/ISO_8601). Normalement, dans Swift, [vous fourniriez](https://www.hackingwithswift.com/example-code/language/how-to-use-iso-8601-dates-with-jsondecoder-and-codable) `JSONDecoder.DateDecodingStrategy.iso8601` en tant que valeur de la propriété `.dateDecodingStrategy` de `JSONDecoder`. Cependant, `CreateChatToken` utilise des fractions de secondes de haute précision dans ses chaînes, une caractéristique qui n'est pas pris en charge par `JSONDecoder.DateDecodingStrategy.iso8601`.

Pour plus de commodité, le kit SDK fournit une extension publique sur `JSONDecoder.DateDecodingStrategy` avec une stratégie `.preciseISO8601` supplémentaire qui vous permet d'utiliser `JSONDecoder` lors du décodage d'une instance de `ChatToken` :

```
import AmazonIVSChatMessaging

// The CreateChatToken data forwarded by your backend
let responseData: Data

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .preciseISO8601
let token = try decoder.decode(ChatToken.self, from: responseData)
```

## Se déconnecter d'une salle de chat
<a name="chat-ios-disconnect-room"></a>

Pour vous déconnecter manuellement d'une instance `ChatRoom` à laquelle vous vous êtes connecté, appelez `room.disconnect()`. Par défaut, les salles de chat appellent automatiquement cette fonction lorsqu'elles sont désallouées.

```
import AmazonIVSChatMessaging

let room = ChatRoom(...)
try await room.connect()

// Disconnect
room.disconnect()
```

## Recevoir un message/événement sur le chat
<a name="chat-ios-receive-message"></a>

Pour envoyer et recevoir des messages dans votre salle de chat, vous devez fournir un objet conforme au protocole `ChatRoomDelegate`, après avoir initialisé une instance de `ChatRoom` et appelé `room.connect()`. Voici un exemple classique utilisant `UIViewController` :

```
import AmazonIVSChatMessaging
import Foundation
import UIKit

class ViewController: UIViewController {
   let room: ChatRoom = ChatRoom(
      awsRegion: "us-west-2",
      tokenProvider: EndpointManager.shared
   )

   override func viewDidLoad() {
      super.viewDidLoad()
      Task { try await setUpChatRoom() }
   }

   private func setUpChatRoom() async throws {
      // Set the delegate to start getting notifications for room events
      room.delegate = self
      try await room.connect()
   }
}

extension ViewController: ChatRoomDelegate {
   func room(_ room: ChatRoom, didReceive message: ChatMessage) { ... }
   func room(_ room: ChatRoom, didReceive event: ChatEvent) { ... }
   func room(_ room: ChatRoom, didDelete message: DeletedMessageEvent) { ... }
}
```

## Recevoir une notification lors d'un changement de connexion
<a name="chat-ios-room-connection-state"></a>

Comme il fallait s'y attendre, vous ne pouvez pas effectuer d'actions telles que l'envoi d'un message dans une salle tant que celle-ci n'est pas complètement connectée. L'architecture du kit SDK tente d'encourager la connexion à une ChatRoom sur un thread d'arrière-plan par le biais d'API asynchrones. Si vous souhaitez créer quelque chose dans votre interface utilisateur qui désactive par exemple un bouton d'envoi de message, le kit SDK propose deux stratégies permettant d'être averti lorsque l'état de connexion d'une salle de chat change, à l'aide de `Combine` ou `ChatRoomDelegate`. Elles sont décrites ci-dessous.

**Important :** l'état de connexion d'une salle de chat peut également changer en raison de facteurs tels qu'une interruption de la connexion réseau. Tenez-en compte lors de la création de votre application.

### Utiliser Combine
<a name="room-connection-state-combine"></a>

Chaque instance de `ChatRoom` est accompagnée de son propre éditeur `Combine` sous la forme de la propriété `state` :

```
import AmazonIVSChatMessaging
import Combine

var cancellables: Set<AnyCancellable> = []

let room = ChatRoom(...)
room.state.sink { state in
   switch state {
   case .connecting:
      let image = UIImage(named: "antenna.radiowaves.left.and.right")
      sendMessageButton.setImage(image, for: .normal)
      sendMessageButton.isEnabled = false
   case .connected:
      let image = UIImage(named: "paperplane.fill")
      sendMessageButton.setImage(image, for: .normal)
      sendMessageButton.isEnabled = true
   case .disconnected:
      let image = UIImage(named: "antenna.radiowaves.left.and.right.slash")
      sendMessageButton.setImage(image, for: .normal)
      sendMessageButton.isEnabled = false
   }
}.assign(to: &cancellables)

// Connect to `ChatRoom` on a background thread
Task(priority: .background) {
   try await room.connect()
}
```

### Utiliser ChatRoomDelegate
<a name="room-connection-state-chatroomdelegate"></a>

Vous pouvez également utiliser les fonctions facultatives `roomDidConnect(_:)`, `roomIsConnecting(_:)` et `roomDidDisconnect(_:)` au sein d'un objet conforme à `ChatRoomDelegate`. Voici un exemple utilisant un `UIViewController` :

```
import AmazonIVSChatMessaging
import Foundation
import UIKit

class ViewController: UIViewController {
   let room: ChatRoom = ChatRoom(
      awsRegion: "us-west-2",
      tokenProvider: EndpointManager.shared
   )

   override func viewDidLoad() {
      super.viewDidLoad()
      Task { try await setUpChatRoom() }
   }

   private func setUpChatRoom() async throws {
      // Set the delegate to start getting notifications for room events
      room.delegate = self
      try await room.connect()
   }
}

extension ViewController: ChatRoomDelegate {
   func roomDidConnect(_ room: ChatRoom) {
      print("room is connected!")
   }
   func roomIsConnecting(_ room: ChatRoom) {
      print("room is currently connecting or fetching a token")
   }
   func roomDidDisconnect(_ room: ChatRoom) {
      print("room disconnected!")
   }
}
```

## Effectuer des actions dans une salle de chat
<a name="chat-ios-room-actions"></a>

Différents utilisateurs disposent de capacités différentes pour les actions qu'ils peuvent effectuer dans une salle de chat : envoyer un message, supprimer un message ou déconnecter un utilisateur, par exemple. Pour effectuer l'une de ces actions, appelez `perform(request:)` sur une `ChatRoom` connectée, en transmettant une instance de l'un des objets `ChatRequest` fournis dans le kit SDK. Les demandes prises en charge se trouvent dans `Request.swift`.

Certaines actions effectuées dans une salle de chat nécessitent que des capacités spécifiques soient accordées aux utilisateurs connectés lorsque votre application backend appelle `CreateChatToken`. De par sa conception, le kit SDK ne peut pas discerner les capacités d'un utilisateur connecté. Par conséquent, bien que vous puissiez tenter d'effectuer des actions de modérateur dans une instance connectée de `ChatRoom`, l'API du plan de contrôle décide à terme si cette action aboutira.

Toutes les actions qui passent par `room.perform(request:)` attendent que la salle reçoive l'instance attendue d'un modèle (dont le type est associé à l'objet de la demande lui-même) correspondant au `requestId` du modèle reçu et de l'objet de la demande. S'il y a un problème avec la demande, `ChatRoom` lance toujours une erreur sous la forme d'un `ChatError`. La définition de `ChatError` se trouve dans `Error.swift`.

### Envoi d'un message
<a name="room-action-send-message"></a>

Pour envoyer un message de chat, utilisez une instance de `SendMessageRequest` :

```
import AmazonIVSChatMessaging

let room = ChatRoom(...)
try await room.connect()
try await room.perform(
   request: SendMessageRequest(
      content: "Release the Kraken!"
   )
)
```

Comme indiqué plus haut, `room.perform(request:)` revient une fois qu'un `ChatMessage` correspondant est reçu par la `ChatRoom`. En cas de problème avec la demande (par exemple, dépassement de la limite de caractères du message pour une salle), une instance de `ChatError` est lancée à la place. Vous pouvez ensuite faire apparaître ces informations utiles dans votre interface utilisateur :

```
import AmazonIVSChatMessaging

do {
   let message = try await room.perform(
      request: SendMessageRequest(
         content: "Release the Kraken!"
      )
   )
   print(message.id)
} catch let error as ChatError {
   switch error.errorCode {
   case .invalidParameter:
      print("Exceeded the character limit!")
   case .tooManyRequests:
      print("Exceeded message request limit!")
   default:
      break
   }

   print(error.errorMessage)
}
```

### Ajouter des métadonnées à un message
<a name="room-action-message-metadata"></a>

Lors de l'[envoi d'un message](#room-action-send-message), vous pouvez ajouter les métadonnées qui lui seront associées. `SendMessageRequest` possède une propriété `attributes`, avec laquelle vous pouvez initialiser votre demande. Les données que vous y associez sont jointes au message lorsque d'autres utilisateurs le reçoivent dans la salle.

Voici un exemple d'ajout de données d'émoticônes à un message en cours d'envoi :

```
import AmazonIVSChatMessaging

let room = ChatRoom(...)
try await room.connect()
try await room.perform(
   request: SendMessageRequest(
      content: "Release the Kraken!",
      attributes: [
         "messageReplyId" : "<other-message-id>",
         "attached-emotes" : "krakenCry,krakenPoggers,krakenCheer"
      ]
   )
)
```

L'utilisation de `attributes` dans une `SendMessageRequest` peut être extrêmement utile pour créer des fonctionnalités complexes dans votre produit de chat. Par exemple, il serait possible de créer une fonctionnalité de threading à l'aide du dictionnaire d'attributs `[String : String]` dans une `SendMessageRequest`.

La charge utile des `attributes` est extrêmement flexible et puissante. Utilisez-la pour obtenir des informations sur votre message que vous ne pourriez pas obtenir autrement. L'utilisation d'attributs est beaucoup plus simple que, par exemple, l'analyse de la chaîne d'un message en vue d'obtenir des informations sur des éléments tels que des émoticônes.

### Supprimer un message
<a name="room-action-delete-message"></a>

La suppression d'un message de chat équivaut à la création d'un tel message. Utilisez la fonction `room.perform(request:)` sur `ChatRoom` pour y parvenir en créant une instance de `DeleteMessageRequest`.

Pour accéder facilement aux instances précédentes des messages de chat reçus, transmettez la valeur de `message.id` à l'initialiseur de `DeleteMessageRequest`.

Vous pouvez éventuellement fournir une chaîne reason à `DeleteMessageRequest` afin de la faire apparaître dans votre interface utilisateur.

```
import AmazonIVSChatMessaging

let room = ChatRoom(...)
try await room.connect()
try await room.perform(
   request: DeleteMessageRequest(
      id: "<other-message-id-to-delete>",
      reason: "Abusive chat is not allowed!"
   )
)
```

Comme il s'agit d'une action de modérateur, votre utilisateur n'a peut-être pas réellement la capacité de supprimer le message d'un autre utilisateur. Vous pouvez utiliser le mécanisme de fonctions lançables de Swift pour faire apparaître un message d'erreur dans votre interface utilisateur lorsqu'un utilisateur tente de supprimer un message sans la capacité appropriée.

Lorsque votre backend appelle `CreateChatToken` pour un utilisateur, il doit transmettre `"DELETE_MESSAGE"` dans le champ `capabilities` afin d'activer cette fonctionnalité pour un utilisateur connecté du chat.

Voici un exemple de détection d'une erreur de capacité lancée lors de la tentative de suppression d'un message sans les autorisations appropriées :

```
import AmazonIVSChatMessaging

do {
   // `deleteEvent` is the same type as the object that gets sent to
   // `ChatRoomDelegate`'s `room(_:didDeleteMessage:)` function
   let deleteEvent = try await room.perform(
      request: DeleteMessageRequest(
         id: "<other-message-id-to-delete>",
         reason: "Abusive chat is not allowed!"
      )
   )
   dataSource.messages[deleteEvent.messageID] = nil
   tableView.reloadData()
} catch let error as ChatError {
   switch error.errorCode {
   case .forbidden:
      print("You cannot delete another user's messages. You need to be a mod to do that!")
   default:
      break
   }

   print(error.errorMessage)
}
```

### Déconnecter un autre utilisateur
<a name="room-action-disconnect-user"></a>

Utilisez `room.perform(request:)` pour déconnecter un autre utilisateur d'une salle de chat. Plus précisément, utilisez une instance de `DisconnectUserRequest`. Tous les `ChatMessage`s reçus par une `ChatRoom` disposent d'une propriété `sender`, qui contient l'ID utilisateur que vous devez initialiser correctement avec une instance de `DisconnectUserRequest`. Vous pouvez éventuellement fournir une chaîne reason pour la demande de déconnexion.

```
import AmazonIVSChatMessaging

let room = ChatRoom(...)
try await room.connect()

let message: ChatMessage = dataSource.messages["<message-id>"]
let sender: ChatUser = message.sender
let userID: String = sender.userId
let reason: String = "You've been disconnected due to abusive behavior"

try await room.perform(
   request: DisconnectUserRequest(
      id: userID,
      reason: reason
   )
)
```

Comme il s'agit d'un autre exemple d'action d'un modérateur, vous pouvez tenter de déconnecter un autre utilisateur, mais vous en serez incapable si vous ne disposez pas de la capacité `DISCONNECT_USER`. La capacité est définie lorsque votre application backend appelle `CreateChatToken` et injecte la chaîne `"DISCONNECT_USER"` dans le champ `capabilities`.

Si votre utilisateur ne dispose pas de la capacité de déconnecter un autre utilisateur, `room.perform(request:)` lance une instance de `ChatError`, tout comme les autres demandes. Vous pouvez inspecter la propriété `errorCode` de l'erreur afin de déterminer si la demande a échoué en raison de l'absence de privilèges de modérateur :

```
import AmazonIVSChatMessaging

do {
   let message: ChatMessage = dataSource.messages["<message-id>"]
   let sender: ChatUser = message.sender
   let userID: String = sender.userId
   let reason: String = "You've been disconnected due to abusive behavior"

   try await room.perform(
      request: DisconnectUserRequest(
         id: userID,
         reason: reason
      )
   )
} catch let error as ChatError {
   switch error.errorCode {
   case .forbidden:
      print("You cannot disconnect another user. You need to be a mod to do that!")
   default:
      break
   }

   print(error.errorMessage)
}
```

# Kit SDK de messagerie client IVS Chat : didacticiel iOS
<a name="chat-sdk-ios-tutorial"></a>

 Le kit SDK de messagerie client Amazon Interactive Video (IVS) Chat pour iOS fournit des interfaces qui vous permettent d'intégrer notre [API de messagerie IVS Chat](https://docs.aws.amazon.com//ivs/latest/chatmsgapireference/welcome.html) (langue française non garantie) sur les plateformes utilisant le [langage de programmation Swift](https://developer.apple.com/swift/) d'Apple. 

 Pour un didacticiel sur le kit SDK Chat pour iOS, consultez [https://aws.github.io/amazon-ivs-chat-messaging-sdk-ios/latest/tutorials/table-of-contents/](https://aws.github.io/amazon-ivs-chat-messaging-sdk-ios/latest/tutorials/table-of-contents/). 

# Kit SDK de messagerie client IVS Chat : guide JavaScript
<a name="chat-sdk-js"></a>



Le kit SDK de messagerie client Amazon Interactive Video (IVS) Chat pour JavaScript vous permet d'intégrer notre [https://docs.aws.amazon.com/ivs/latest/chatmsgapireference/welcome.html](https://docs.aws.amazon.com/ivs/latest/chatmsgapireference/welcome.html) sur les plateformes utilisant un navigateur Web.

**Dernière version du kit SDK de messagerie client IVS Chat pour JavaScript :** 1.0.2 ([Notes de mise à jour](https://docs.aws.amazon.com//ivs/latest/ChatUserGuide/release-notes.html#nov09-22))

**Documentation de référence :** pour plus d'informations sur les méthodes les plus importantes disponibles dans le kit SDK de messagerie client Amazon IVS Chat pour JavaScript, veuillez consulter la documentation de référence à l'adresse : [https://aws.github.io/amazon-ivs-chat-messaging-sdk-js/1.0.2/](https://aws.github.io/amazon-ivs-chat-messaging-sdk-js/1.0.2/)

**Exemple de code :** consultez l'exemple de référentiel sur GitHub, pour une démonstration spécifique au Web utilisant le SDK JavaScript : [https://github.com/aws-samples/amazon-ivs-chat-web-demo](https://github.com/aws-samples/amazon-ivs-chat-web-demo)

# Démarrez avec le SDK de messagerie client Chat IVS pour JavaScript
<a name="chat-js-getting-started"></a>

Avant de commencer, vous devez être familiarisé avec la [Mise en route avec Chat Amazon IVS](getting-started-chat.md).

## Ajouter le package
<a name="chat-js-add-package"></a>

Utilisez soit :

```
$ npm install --save amazon-ivs-chat-messaging
```

ou :

```
$ yarn add amazon-ivs-chat-messaging
```

## React Native Support
<a name="chat-js-react-native-support"></a>

Le kit SDK de messagerie du client IVS Chat pour JavaScript possède une dépendance `uuid` qui utilise la méthode `crypto.getRandomValues`. Comme cette méthode n'est pas prise en charge dans React Native, vous devez installer le polyfill supplémentaire `react-native-get-random-value` et l'importer en haut du fichier `index.js` :

```
import 'react-native-get-random-values';
import {AppRegistry} from 'react-native';
import App from './src/App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);
```

## Configuration de votre backend
<a name="chat-js-setup-backend"></a>

Cette intégration nécessite des points de terminaison sur votre serveur qui communiquent avec l'[API Chat Amazon IVS](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/Welcome.html). Utilisez les [bibliothèques AWS officielles](https://aws.amazon.com/developer/tools/) pour accéder à l'API Amazon IVS depuis votre serveur. Elles sont accessibles dans plusieurs langues depuis les packages publics, par exemple, [node.js](https://www.npmjs.com/package/aws-sdk), [java](https://github.com/aws/aws-sdk-java) et [go](https://github.com/aws/aws-sdk-go).

Créez un point de terminaison de serveur qui communique avec l’opération d’API Chat Amazon IVS [CreateChatToken](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_CreateChatToken.html), afin de créer un jeton de chat pour les utilisateurs de chat.

# Utilisation de l'SDK de messagerie client Chat IVS pour JavaScript
<a name="chat-js-using-sdk"></a>

Ce document explique les étapes nécessaires à l'utilisation de l'SDK de messagerie client Chat Amazon IVS pour JavaScript.

## Initialiser une instance de salle de chat
<a name="chat-js-initialize-room"></a>

Créez une instance de la classe `ChatRoom`. Cela nécessite de passer `regionOrUrl` (la région AWS dans laquelle votre salle de chat est hébergée) et `tokenProvider` (la méthode de récupération des jetons sera créée à l'étape suivante) : 

```
const room = new ChatRoom({
  regionOrUrl: 'us-west-2',
  tokenProvider: tokenProvider,
});
```

## Fonction de fournisseur de jetons
<a name="chat-js-token-provider-function"></a>

Créez une fonction fournisseur de jetons asynchrone qui récupère un jeton de chat depuis votre backend :

```
type ChatTokenProvider = () => Promise<ChatToken>;
```

La fonction ne doit accepter aucun paramètre et renvoyer une [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) contenant un objet jeton de discussion :

```
type ChatToken = {
  token: string;
  sessionExpirationTime?: Date;
  tokenExpirationTime?: Date;
}
```

Cette fonction est nécessaire pour [initialiser l'objet ChatRoom](#chat-js-initialize-room). Ci-dessous, renseignez les champs `<token>` et `<date-time>` avec les valeurs reçues de votre backend :

```
// You will need to fetch a fresh token each time this method is called by
// the IVS Chat Messaging SDK, since each token is only accepted once.
function tokenProvider(): Promise<ChatToken> {
  // Call your backend to fetch chat token from IVS Chat endpoint: 
  // e.g. const token = await appBackend.getChatToken()
  return {
    token: "<token>",
    sessionExpirationTime: new Date("<date-time>"),
    tokenExpirationTime: new Date("<date-time>")
  }
}
```

 N'oubliez pas de transmettre le `tokenProvider` au constructeur de ChatRoom. ChatRoom actualise le jeton lorsque la connexion est interrompue ou que la session expire. N'utilisez pas le `tokenProvider` pour stocker un jeton où que ce soit ; le ChatRoom le gère pour vous. 

## Recevoir des événements
<a name="chat-js-receive-event"></a>

Abonnez-vous ensuite aux événements de la salle de chat pour recevoir les événements du cycle de vie, ainsi que les messages et les événements diffusés dans le salle de chat :

```
/**
* Called when room is establishing the initial connection or reestablishing
* connection after socket failure/token expiration/etc
*/
const unsubscribeOnConnecting = room.addListener('connecting', () => { });

/** Called when connection has been established. */
const unsubscribeOnConnected = room.addListener('connect', () => { });

/** Called when a room has been disconnected. */
const unsubscribeOnDisconnected = room.addListener('disconnect', () => { });

/** Called when a chat message has been received. */
const unsubscribeOnMessageReceived = room.addListener('message', (message) => {
 /* Example message:
  * {
  *   id: "5OPsDdX18qcJ",
  *   sender: { userId: "user1" },
  *   content: "hello world",
  *   sendTime: new Date("2022-10-11T12:46:41.723Z"),
  *   requestId: "d1b511d8-d5ed-4346-b43f-49197c6e61de"
  * }
  */
});

/** Called when a chat event has been received. */
const unsubscribeOnEventReceived = room.addListener('event', (event) => {
 /* Example event:
  * {
  *   id: "5OPsDdX18qcJ",
  *   eventName: "customEvent,
  *   sendTime: new Date("2022-10-11T12:46:41.723Z"),
  *   requestId: "d1b511d8-d5ed-4346-b43f-49197c6e61de",
  *   attributes: { "Custom Attribute": "Custom Attribute Value" }
  * }
  */
});

/** Called when `aws:DELETE_MESSAGE` system event has been received. */
const unsubscribeOnMessageDelete = room.addListener('messageDelete', (deleteMessageEvent) => {
 /* Example delete message event:
  * {
  *   id: "AYk6xKitV4On",
  *   messageId: "R1BLTDN84zEO",
  *   reason: "Spam",
  *   sendTime: new Date("2022-10-11T12:56:41.113Z"),
  *   requestId: "b379050a-2324-497b-9604-575cb5a9c5cd",
  *   attributes: { MessageID: "R1BLTDN84zEO", Reason: "Spam" }
  * }
  */
});

/** Called when `aws:DISCONNECT_USER` system event has been received. */
const unsubscribeOnUserDisconnect = room.addListener('userDisconnect', (disconnectUserEvent) => {
 /* Example event payload:
  * {
  *   id: "AYk6xKitV4On",
  *   userId": "R1BLTDN84zEO",
  *   reason": "Spam",
  *   sendTime": new Date("2022-10-11T12:56:41.113Z"),
  *   requestId": "b379050a-2324-497b-9604-575cb5a9c5cd",
  *   attributes": { UserId: "R1BLTDN84zEO", Reason: "Spam" }
  * }
  */
});
```

## Se connecter à une salle de chat
<a name="chat-js-connect-to-chat-room"></a>

La dernière étape de l'initialisation de base consiste à se connecter à la salle de chat spécifique en établissant une connexion WebSocket. Pour ce faire, appelez la méthode `connect()` au sein de l'instance de la salle :

```
room.connect();
```

 Le kit SDK tentera d'établir une connexion à une salle de chat codée dans le jeton de chat reçu de votre serveur. 

 Une fois que vous aurez appelé `connect()`, la salle passera à l'état `connecting` et émettra un événement `connecting`. Lorsque la salle se connecte avec succès, elle passe à l'état `connected` et émet un événement `connect`. 

 Un échec de connexion peut survenir en raison de problèmes lors de la récupération du jeton ou lors de la connexion à WebSocket. Dans ce cas, la salle essaie de se reconnecter automatiquement jusqu'au nombre de fois indiqué par le paramètre du constructeur `maxReconnectAttempts`. Lors des tentatives de reconnexion, la salle est à l'état `connecting` et n'émet aucun événement supplémentaire. Après avoir épuisé les tentatives de reconnexion, la pièce passe à l'état `disconnected` et émet un événement `disconnect` (avec une raison de déconnexion pertinente). Dans l'état `disconnected`, la salle n'essaie plus de se connecter ; vous devez appeler `connect()` à nouveau pour déclencher le processus de connexion. 

## Effectuer des actions dans une salle de chat
<a name="chat-js-room-actions"></a>

Le kit SDK de messagerie chat Amazon IVS fournit aux utilisateurs des actions permettant d'envoyer des messages, de supprimer des messages et de déconnecter les autres utilisateurs. Ils sont disponibles sur l'instance `ChatRoom`. Ils renvoient un objet `Promise` qui vous permet de recevoir une confirmation ou un rejet de la demande. 

### Envoi d'un message
<a name="chat-js-room-actions-send-message"></a>

Pour cette demande, la fonctionnalité `SEND_MESSAGE` doit être encodée dans votre jeton de chat.

Pour déclencher une demande send-message :

```
const request = new SendMessageRequest('Test Echo');
room.sendMessage(request);
```

Pour obtenir une confirmation ou un rejet de la demande, `await` la promesse retournée ou utilisez la méthode `then()` :

```
try {
  const message = await room.sendMessage(request);
  // Message was successfully sent to chat room
} catch (error) {
  // Message request was rejected. Inspect the `error` parameter for details.
}
```

### Supprimer un message
<a name="chat-js-room-actions-delete-message"></a>

Pour cette demande, la fonctionnalité `DELETE_MESSAGE` doit être encodée dans votre jeton de chat.

Pour supprimer un message à des fins de modération, appelez la méthode `deleteMessage()` : 

```
const request = new DeleteMessageRequest(messageId, 'Reason for deletion');
room.deleteMessage(request);
```

Pour obtenir une confirmation ou un rejet de la demande, `await` la promesse retournée ou utilisez la méthode `then()` :

```
try {
  const deleteMessageEvent = await room.deleteMessage(request);
  // Message was successfully deleted from chat room
} catch (error) {
  // Delete message request was rejected. Inspect the `error` parameter for details.
}
```

### Déconnecter un autre utilisateur
<a name="chat-js-room-actions-disconnect-user"></a>

Pour cette demande, la fonctionnalité `DISCONNECT_USER` doit être encodée dans votre jeton de chat.

Pour déconnecter un autre utilisateur à des fins de modération, appelez la méthode `disconnectUser()` : 

```
const request = new DisconnectUserRequest(userId, 'Reason for disconnecting user');
room.disconnectUser(request);
```

Pour obtenir une confirmation ou un rejet de la demande, `await` la promesse retournée ou utilisez la méthode `then()` :

```
try {
  const disconnectUserEvent = await room.disconnectUser(request);
  // User was successfully disconnected from the chat room
} catch (error) {
  // Disconnect user request was rejected. Inspect the `error` parameter for details.
}
```

## Se déconnecter d'une salle de chat
<a name="chat-js-disconnect-room"></a>

Pour fermer votre connexion à la salle de chat, appelez la méthode `disconnect()` sur l'instance `room` :

```
room.disconnect();
```

L'appel de cette méthode entraîne la fermeture ordonnée du WebSocket sous-jacent par la salle de manière ordonnée. L'instance de salle passe à un état `disconnected` et émet un événement de déconnexion, la raison `disconnect` étant définie sur `"clientDisconnect"`. 

# Kit SDK de messagerie client IVS Chat : didacticiel JavaScript, partie 1 : salles de chat
<a name="chat-sdk-js-tutorial-chat-rooms"></a>

Il s'agit de la première partie d'un didacticiel en deux volets. Vous apprendrez les bases de l'utilisation du kit SDK de messagerie client Amazon IVS Chat pour JavaScript en créant une application entièrement fonctionnelle utilisant JavaScript/TypeScript. Nous appelons l'application *Chatterbox*.

Le public cible est constitué de développeurs expérimentés qui découvrent le kit SDK de messagerie Amazon IVS Chat. Vous devez être à l'aise avec le langage de programmation JavaScript/TypeScript et la bibliothèque React.

Par souci de concision, nous ferons référence au kit SDK de messagerie client Amazon IVS Chat pour JavaScript en le nommant kit SDK Chat JS.

**Remarque** : dans certains cas, les exemples de code pour JavaScript et TypeScript sont identiques et sont donc combinés.

Cette première partie du didacticiel est divisée en plusieurs sections :

1. [Configurer un serveur d'authentification/d'autorisation local](#chat-js-rooms-auth-server)

1. [Créer un projet Chatterbox](#chat-js-rooms-chatterbox)

1. [Se connecter à une salle de chat](#chat-js-rooms-connect)

1. [Créer un fournisseur de jetons](#chat-js-rooms-token-provider)

1. [Observer les mises à jour de la connexion](#chat-js-rooms-connection-state)

1. [Créer un composant de bouton d'envoi](#chat-js-rooms-send-button)

1. [Créer une entrée de message](#chat-js-rooms-message-input)

1. [Étapes suivantes](#chat-js-rooms-next-steps)

Pour une documentation complète sur le kit SDK, commencez par le [kit SDK de messagerie client Amazon IVS Chat](chat-sdk.md) (ici dans le *Guide de l'utilisateur Chat Amazon IVS*) et la [Messagerie du client de chat : référence du kit SDK pour JavaScript](https://aws.github.io/amazon-ivs-chat-messaging-sdk-js/latest/) (sur GitHub).

## Conditions préalables
<a name="chat-js-rooms-prerequisites"></a>
+ Familiarisez-vous avec JavaScript/TypeScript et la bibliothèque React. Si vous ne connaissez pas React Native, découvrez les bases dans Introduction à React Native ([langue non prévue](https://react.dev/learn/tutorial-tic-tac-toe)).
+ Lisez et assurez-vous d'avoir compris [Mise en route avec le chat Amazon IVS](getting-started-chat.md).
+ Créez un utilisateur AWS IAM avec les fonctionnalités CreateChatToken et CreateRoom définies dans une politique IAM existante. (Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md).)
+ Assurez-vous que les clés secrètes et d'accès de cet utilisateur sont stockées dans un fichier d'informations d'identification AWS. Pour obtenir des instructions, consultez le [Guide de l'utilisateur de l'interface de ligne de commande AWS](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) (en particulier les [paramètres de configuration et de fichier d'informations d'identification](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)).
+ Créez une salle de chat et enregistrez son ARN. Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md). (Si vous n'enregistrez pas l'ARN, vous pourrez le consulter ultérieurement à l'aide de la console ou de l'API Chat.)
+ Installez l'environnement Node.js 14\$1 avec le gestionnaire de packages NPM ou Yarn.

## Configurer un serveur d'authentification/d'autorisation local
<a name="chat-js-rooms-auth-server"></a>

Votre application backend est chargée à la fois de créer des salles de chat et de générer les jetons de chat nécessaires au kit SDK Chat JS pour authentifier et autoriser vos clients à accéder à vos salles de chat. Vous devez utiliser votre propre backend, car vous ne pouvez pas stocker de manière sécurisée les clés AWS dans une application mobile ; des attaquants avertis pourraient les extraire et accéder à votre compte AWS.

Consultez la section [Create a Chat Token](getting-started-chat-auth.md) (Créer un jeton de chat) dans *Mise en route avec Amazon IVS Chat*. Comme le montre l'organigramme, votre application côté serveur est chargée de créer un jeton de chat. Cela signifie que votre application doit fournir ses propres moyens de générer un jeton de chat en demandant un jeton à votre application côté serveur.

Dans cette section, vous apprendrez les bases de la création d'un fournisseur de jetons dans votre backend. Nous utilisons l'infrastructure express pour créer un serveur local en direct qui gère la création de jetons de chat à l'aide de votre environnement AWS local.

Créez un projet `npm` vide à l'aide de NPM. Créez un répertoire pour héberger votre application et faites-en votre répertoire de travail :

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

Utilisez `npm init` pour créer un fichier `package.json` pour votre application :

```
$ npm init
```

Cette commande vous invite à fournir plusieurs informations, notamment le nom et la version de votre application. Pour l'instant, appuyez simplement sur **ENTRÉE** pour accepter les valeurs par défaut pour la plupart d'entre elles, à l'exception de la suivante :

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

Appuyez sur **ENTRÉE** pour accepter le nom de fichier par défaut `index.js` suggéré ou saisissez le nom que vous souhaitez donner au fichier principal.

Installez maintenant les dépendances obligatoires :

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

`aws-sdk` nécessite des variables d'environnement de configuration, qui se chargent automatiquement à partir d'un fichier nommé `.env` situé dans le répertoire racine. Pour le configurer, créez un fichier nommé `.env` et renseignez les informations de configuration manquantes :

```
# .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=...
```

Créons maintenant un fichier de point d'entrée dans le répertoire racine avec le nom que vous avez saisi ci-dessus dans la commande `npm init`. Dans ce cas, nous utilisons `index.js` et importons tous les packages requis :

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

Créez maintenant une nouvelle instance de `express` :

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

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

Ensuite, vous pouvez créer votre première méthode POST de point de terminaison pour le fournisseur de jetons. Prenez les paramètres requis dans le corps de la demande (`roomId`, `userId`, `capabilities` et `sessionDurationInMinutes`) :

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

Ajoutez la validation des champs obligatoires :

```
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;
  }
});
```

Après avoir préparé la méthode POST, nous intégrons `createChatToken` avec `aws-sdk` pour la fonctionnalité de base d'authentification/autorisation :

```
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 });
    }
  });
});
```

À la fin du fichier, ajoutez un écouteur de port pour votre application `express` :

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

À présent, vous pouvez exécuter le serveur à l'aide de la commande suivante depuis la racine du projet :

```
$ node index.js
```

**Conseil** : ce serveur accepte les requêtes URL sur https://localhost:3000.

## Créer un projet Chatterbox
<a name="chat-js-rooms-chatterbox"></a>

Vous créez d'abord le projet React appelé `chatterbox`. Exécutez cette commande : 

```
npx create-react-app chatterbox
```

Vous pouvez intégrer le kit SDK de messagerie client Chat pour JS via [Node Package Manager](https://www.npmjs.com/) ou [Yarn Package Manager](https://yarnpkg.com/) :
+ Npm : `npm install amazon-ivs-chat-messaging`
+ Yarn : `yarn add amazon-ivs-chat-messaging`

## Se connecter à une salle de chat
<a name="chat-js-rooms-connect"></a>

Ici, vous créez une `ChatRoom` et vous vous y connectez à l'aide de méthodes asynchrones. La classe `ChatRoom` gère la connexion de votre utilisateur au kit SDK Chat JS. Pour vous connecter correctement à une salle de chat, vous devez fournir une instance de `ChatToken` dans votre application React.

Accédez au fichier `App` créé dans le projet `chatterbox` par défaut et supprimez tout ce qui se trouve entre les deux balises `<div>`. Aucun code prérempli n'est nécessaire. À ce stade, notre `App` est assez vide.

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

import * as React from 'react';

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

Créez une instance `ChatRoom` et transmettez-la à l'état à l'aide du hook `useState`. Cela nécessite de passer `regionOrUrl` (la région AWS dans laquelle votre salle de chat est hébergée) et `tokenProvider` (utilisé pour le flux d'authentification/autorisation backend qui est créé dans les étapes suivantes).

**Important** : vous devez utiliser la même région AWS que celle dans laquelle vous avez créé la salle dans la [Mise en route avec Amazon IVS Chat](getting-started-chat-create-room.md). L'API est un service régional AWS. Pour obtenir la liste des régions prises en charge et des points de terminaison du service HTTPS Amazon IVS Chat, consultez la page des [régions Amazon IVS Chat](https://docs.aws.amazon.com/general/latest/gr/ivs.html#ivs_region).

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

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

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

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

## Créer un fournisseur de jetons
<a name="chat-js-rooms-token-provider"></a>

À l'étape suivante, nous devons créer une fonction `tokenProvider` sans paramètre requise par le constructeur `ChatRoom`. Tout d'abord, nous allons créer une fonction `fetchChatToken` qui enverra une requête POST à l'application backend que vous avez configurée dans [Configurer un serveur d'authentification/d'autorisation local](#chat-js-rooms-auth-server). Les jetons de chat contiennent les informations nécessaires au kit SDK pour établir avec succès une connexion à la salle de chat. L'API de chat utilise ces jetons comme moyen sécurisé de valider l'identité d'un utilisateur, ses capacités au sein d'une salle de chat et la durée de la session.

Dans le navigateur de projet, créez un fichier TypeScript/JavaScript nommé `fetchChatToken`. Créez une demande de récupération à l'application `backend` et renvoyez l'objet `ChatToken` de la réponse. Ajoutez les propriétés du corps de la demande nécessaires à la création d'un jeton de chat. Utilisez les règles définies pour les [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html). Ces propriétés sont documentées dans l’opération [CreateChatToken](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_CreateChatToken.html#API_CreateChatToken_RequestBody).

**Remarque** : l'URL que vous utilisez ici est la même que celle que votre serveur local a créée lorsque vous avez exécuté l'application 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),
  };
}
```

------

## Observer les mises à jour de la connexion
<a name="chat-js-rooms-connection-state"></a>

Réagir aux modifications de l'état de connexion d'une salle de chat est un élément essentiel de la création d'une application de chat. Commençons par nous abonner aux événements pertinents :

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

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

export default function App() {
  const [room] = useState(
    () =>
      new ChatRoom({
        regionOrUrl: process.env.REGION as string,
        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 <div>Hello!</div>;
}
```

Ensuite, nous devons fournir la possibilité de lire l'état de la connexion. Nous utilisons notre hook `useState` pour créer un état local dans `App` et définir l'état de connexion dans chaque écouteur.

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

```
// App.tsx

import React, { useState, useEffect } from 'react';
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 as string,
        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 <div>Hello!</div>;
}
```

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

```
// App.jsx

import React, { useState, useEffect } from 'react';
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']),
      }),
  );
  const [connectionState, setConnectionState] = useState('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 <div>Hello!</div>;
}
```

------

Après vous être abonné à l'état de connexion, affichez l'état de la connexion et connectez-vous à la salle de chat en utilisant la méthode `room.connect` indiquée dans le hook `useEffect` :

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

// ...

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 (
  <div>
    <h4>Connection State: {connectionState}</h4>
  </div>
);

// ...
```

Vous avez correctement mis en place une connexion à la salle de chat.

## Créer un composant de bouton d'envoi
<a name="chat-js-rooms-send-button"></a>

Dans cette section, vous créez un bouton d'envoi qui a une apparence différente pour chaque état de connexion. Le bouton d'envoi facilite l'envoi de messages dans une salle de chat. Il sert également d'indicateur visuel indiquant si et quand des messages peuvent être envoyés, par exemple en cas de perte de connexion ou de sessions de chat expirées.

Commencez par créer un fichier dans le répertoire `src` de votre projet Chatterbox et nommez-le `SendButton`. Ensuite, créez un composant qui affichera un bouton pour votre application de chat. Exportez votre `SendButton` et importez-le dans `App`. Dans le champ `<div></div>` vide, ajoutez `<SendButton />`.

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

```
// SendButton.tsx

import React from 'react';

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

export const SendButton = ({ onPress, disabled }: Props) => {
  return (
    <button disabled={disabled} onClick={onPress}>
      Send
    </button>
  );
};

// App.tsx

import { SendButton } from './SendButton';

// ...

return (
  <div>
    <div>Connection State: {connectionState}</div>
    <SendButton />
  </div>
);
```

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

```
// SendButton.jsx

import React from 'react';

export const SendButton = ({ onPress, disabled }) => {
  return (
    <button disabled={disabled} onClick={onPress}>
      Send
    </button>
  );
};

// App.jsx

import { SendButton } from './SendButton';

// ...

return (
  <div>
    <div>Connection State: {connectionState}</div>
    <SendButton />
  </div>
);
```

------

Ensuite, dans `App`, définissez une fonction nommée `onMessageSend` et transmettez-la à la propriété `SendButton onPress`. Définissez une autre variable nommée `isSendDisabled` (qui empêche l'envoi de messages lorsque la salle n'est pas connectée) et transmettez-la à la propriété `SendButton disabled`.

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

// ...

const onMessageSend = () => {};

const isSendDisabled = connectionState !== 'connected';

return (
  <div>
    <div>Connection State: {connectionState}</div>
    <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
  </div>
);

// ...
```

## Créer une entrée de message
<a name="chat-js-rooms-message-input"></a>

La barre de messages Chatterbox est le composant avec lequel vous allez interagir pour envoyer des messages à une salle de chat. Elle contient généralement une entrée de texte pour rédiger votre message et un bouton pour envoyer votre message.

Pour créer un composant `MessageInput`, créez d'abord un fichier dans le répertoire `src` et nommez-le `MessageInput`. Ensuite, créez un composant d'entrée contrôlé qui affichera une entrée pour votre application de chat. Exportez votre `MessageInput` et importez-le dans `App` (au-dessus du `<SendButton />`).

Créez un état nommé `messageToSend` à l'aide du hook `useState`, avec une chaîne vide comme valeur par défaut. Dans le corps de votre application, passez `messageToSend` à `value` de `MessageInput` et transmettez `setMessageToSend` à la propriété `onMessageChange` :

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

```
// MessageInput.tsx

import * as React from 'react';

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

export const MessageInput = ({ value, onValueChange }: Props) => {
  return (
    <input type="text" value={value} onChange={(e) => onValueChange?.(e.target.value)} placeholder="Send a message" />
  );
};


// App.tsx

// ...  

import { MessageInput } from './MessageInput';

// ...

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

// ...

return (
  <div>
    <h4>Connection State: {connectionState}</h4>
    <MessageInput value={messageToSend} onMessageChange={setMessageToSend} />
    <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
  </div>
);
```

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

```
// MessageInput.jsx

import * as React from 'react';

export const MessageInput = ({ value, onValueChange }) => {
  return (
    <input type="text" value={value} onChange={(e) => onValueChange?.(e.target.value)} placeholder="Send a message" />
  );
};

// App.jsx

// ...  

import { MessageInput } from './MessageInput';

// ...

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

// ...


return (
  <div>
    <h4>Connection State: {connectionState}</h4>
    <MessageInput value={messageToSend} onMessageChange={setMessageToSend} />
    <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
  </div>
);
```

------

## Étapes suivantes
<a name="chat-js-rooms-next-steps"></a>

Maintenant que vous avez fini de créer une barre de messages pour Chatterbox, passez à la seconde partie de ce didacticiel JavaScript, [Messages et événements](chat-sdk-js-tutorial-messages-events.md).

# Kit SDK de messagerie client IVS Chat : didacticiel JavaScript, partie 2 : messages et événements
<a name="chat-sdk-js-tutorial-messages-events"></a>

Cette seconde et dernière partie du didacticiel est divisée en plusieurs sections :

1. [S'abonner aux événements des messages de chat](#chat-js-messages-events-subscribe)

1. [Afficher les messages reçus](#chat-js-messages-events-show)

   1.  [Créer un composant de message](#chat-js-messages-create-component)

   1. [Reconnaître les messages envoyés par l'utilisateur actuel](#chat-js-messages-recognize)

   1. [Créer un composant de liste de messages](#chat-js-messages-create-list-component)

   1. [Afficher une liste de messages de chat](#chat-js-messages-render-list)

1. [Effectuer des actions dans une salle de chat](#chat-js-messages-events-room-actions)

   1. [Envoi d'un message](#chat-js-room-actions-sending-message)

   1. [Supprimer un message](#chat-js-room-actions-deleting-message)

1. [Étapes suivantes](#chat-js-messages-events-next-steps)

**Remarque** : dans certains cas, les exemples de code pour JavaScript et TypeScript sont identiques et sont donc combinés.

Pour une documentation complète sur le kit SDK, commencez par le [kit SDK de messagerie client Amazon IVS Chat](chat-sdk.md) (ici dans le *Guide de l'utilisateur Chat Amazon IVS*) et la [Messagerie du client de chat : référence du kit SDK pour JavaScript](https://aws.github.io/amazon-ivs-chat-messaging-sdk-js/latest/) (sur GitHub).

## Prérequis
<a name="chat-js-messages-events-prerequisite"></a>

Assurez-vous d'avoir terminé la première partie de ce didacticiel relative aux [Salles de chat](chat-sdk-js-tutorial-chat-rooms.md).

## S'abonner aux événements des messages de chat
<a name="chat-js-messages-events-subscribe"></a>

L'instance `ChatRoom` utilise des événements pour communiquer lorsque des événements se produisent dans une salle de chat. Pour commencer à mettre en œuvre l'expérience de chat, vous devez montrer à vos utilisateurs quand d'autres personnes envoient un message dans la salle à laquelle ils sont connectés.

Ici, vous vous abonnez aux événements des messages de chat. Plus tard, nous vous montrerons comment mettre à jour une liste de messages que vous créez, qui est mise à jour à chaque message/événement.

Dans votre `App`, dans le hook `useEffect`, abonnez-vous à tous les événements de message :

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

useEffect(() => {
  // ...
  const unsubscribeOnMessageReceived = room.addListener('message', (message) => {});

  return () => {
    // ...
    unsubscribeOnMessageReceived();
  };
}, []);
```

## Afficher les messages reçus
<a name="chat-js-messages-events-show"></a>

La réception de messages est au cœur de l'expérience de chat. À l'aide du kit SDK Chat JS, vous pouvez configurer votre code pour recevoir facilement les événements des autres utilisateurs connectés à une salle de chat.

Plus tard, nous vous montrerons comment effectuer des actions dans une salle de chat qui tirent parti des composants que vous créez ici.

Dans votre `App`, définissez un état nommé `messages` avec un type de tableau `ChatMessage` nommé `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

// ...

export default function App() {
  const [messages, setMessages] = useState([]);

  //...
}
```

------

Ensuite, dans la fonction d'écouteur `message`, ajoutez `message` au tableau `messages` :

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

// ...

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

// ...
```

Ci-dessous, nous passons en revue les tâches pour afficher les messages reçus :

1.  [Créer un composant de message](#chat-js-messages-create-component)

1. [Reconnaître les messages envoyés par l'utilisateur actuel](#chat-js-messages-recognize)

1. [Créer un composant de liste de messages](#chat-js-messages-create-list-component)

1. [Afficher une liste de messages de chat](#chat-js-messages-render-list)

### Créer un composant de message
<a name="chat-js-messages-create-component"></a>

Le composant `Message` est chargé de rendre le contenu d'un message reçu par votre salle de chat. Dans cette section, vous créez un composant de messages pour afficher les messages de chat individuels dans l'`App`.

Dans le répertoire `src`, créez un fichier nommé `Message`. Transmettez le type `ChatMessage` de ce composant et transmettez la chaîne `content` provenant des propriétés `ChatMessage` pour afficher le texte du message reçu des écouteurs de messages de la salle de chat. Dans le navigateur de projets, accédez à `Message`.

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

```
// Message.tsx

import * as React from 'react';
import { ChatMessage } from 'amazon-ivs-chat-messaging';

type Props = {
  message: ChatMessage;
}

export const Message = ({ message }: Props) => {
  return (
    <div style={{ backgroundColor: 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
    </div>
  );
};
```

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

```
// Message.jsx

import * as React from 'react';

export const Message = ({ message }) => {
  return (
    <div style={{ backgroundColor: 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
    </div>
  );
};
```

------

Conseil : utilisez ce composant pour stocker les différentes propriétés que vous souhaitez afficher dans les lignes de vos messages, par exemple, les URL des avatars, les noms d'utilisateur et les horodatages de l'envoi du message.

### Reconnaître les messages envoyés par l'utilisateur actuel
<a name="chat-js-messages-recognize"></a>

Pour reconnaître le message envoyé par l'utilisateur actuel, nous modifions le code et créons un contexte React pour stocker le `userId` de l'utilisateur actuel.

Dans le répertoire `src`, créez un fichier nommé `UserContext` :

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

```
// UserContext.tsx

import React, { ReactNode, useState, useContext, createContext } from 'react';

type UserContextType = {
  userId: string;
  setUserId: (userId: string) => void;
};

const UserContext = createContext<UserContextType | undefined>(undefined);

export const useUserContext = () => {
  const context = useContext(UserContext);

  if (context === undefined) {
    throw new Error('useUserContext must be within UserProvider');
  }

  return context;
};

type UserProviderType = {
  children: ReactNode;
}

export const UserProvider = ({ children }: UserProviderType) => {
  const [userId, setUserId] = useState('Mike');

  return <UserContext.Provider value={{ userId, setUserId }}>{children}</UserContext.Provider>;
};
```

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

```
// UserContext.jsx

import React, { useState, useContext, createContext } from 'react';

const UserContext = createContext(undefined);

export const useUserContext = () => {
  const context = useContext(UserContext);

  if (context === undefined) {
    throw new Error('useUserContext must be within UserProvider');
  }

  return context;
};

export const UserProvider = ({ children }) => {
  const [userId, setUserId] = useState('Mike');

  return <UserContext.Provider value={{ userId, setUserId }}>{children}</UserContext.Provider>;
};
```

------

Remarque : ici, nous avons utilisé le hook `useState` pour stocker la valeur `userId`. Dorénavant, vous pourrez utiliser `setUserId` pour modifier le contexte de l'utilisateur ou à des fins de connexion.

Ensuite, remplacez `userId` dans le premier paramètre transmis à `tokenProvider`, en utilisant le contexte créé précédemment :

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

  // ...
}
```

Dans votre composant `Message`, utilisez le `UserContext` créé auparavant, déclarez la variable `isMine`, associez le `userId` de l'expéditeur au `userId` du contexte et appliquez différents styles de messages à l'utilisateur actuel.

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

```
// Message.tsx

import * as React from 'react';
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 (
    <div style={{ backgroundColor: isMine ? 'lightblue' : 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
    </div>
  );
};
```

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

```
// Message.jsx

import * as React from 'react';
import { useUserContext } from './UserContext';

export const Message = ({ message }) => {
  const { userId } = useUserContext();

  const isMine = message.sender.userId === userId;

  return (
    <div style={{ backgroundColor: isMine ? 'lightblue' : 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
    </div>
  );
};
```

------

### Créer un composant de liste de messages
<a name="chat-js-messages-create-list-component"></a>

Le composant `MessageList` est chargé d'afficher la conversation d'une salle de chat au fil du temps. Le fichier `MessageList` est le conteneur qui conserve tous nos messages. `Message` est une ligne dans `MessageList`.

Dans le répertoire `src`, créez un fichier nommé `MessageList`. Définissez `Props` avec des `messages` de type tableau `ChatMessage`. À l'intérieur du corps, mappez notre propriété `messages` et transmettez `Props` à votre composant `Message`.

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

```
// MessageList.tsx

import React from 'react';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { Message } from './Message';

interface Props {
  messages: ChatMessage[];
}

export const MessageList = ({ messages }: Props) => {
  return (
    <div>
      {messages.map((message) => (
        <Message key={message.id} message={message}/>
      ))}
    </div>
  );
};
```

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

```
// MessageList.jsx

import React from 'react';
import { Message } from './Message';

export const MessageList = ({ messages }) => {
  return (
    <div>
      {messages.map((message) => (
        <Message key={message.id} message={message} />
      ))}
    </div>
  );
};
```

------

### Afficher une liste de messages de chat
<a name="chat-js-messages-render-list"></a>

Ajoutez maintenant votre nouveau composant `MessageList` à votre composant principal `App` :

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

import { MessageList } from './MessageList';
// ...

return (
  <div style={{ display: 'flex', flexDirection: 'column', padding: 10 }}>
    <h4>Connection State: {connectionState}</h4>
    <MessageList messages={messages} />
    <div style={{ flexDirection: 'row', display: 'flex', width: '100%', backgroundColor: 'red' }}>
      <MessageInput value={messageToSend} onValueChange={setMessageToSend} />
      <SendButton disabled={isSendDisabled} onPress={onMessageSend} />
    </div>
  </div>
);

// ...
```

Toutes les pièces du puzzle sont maintenant en place pour que votre `App` commence à afficher les messages reçus par votre salle de chat. Continuez ci-dessous pour découvrir comment réaliser des actions dans une salle de chat qui tirent parti des composants que vous avez créés.

## Effectuer des actions dans une salle de chat
<a name="chat-js-messages-events-room-actions"></a>

L'envoi de messages et l'exécution d'actions de modérateur dans une salle de chat sont quelques-uns des principaux moyens d'interagir avec une salle de chat. Vous apprendrez ici comment utiliser divers objets `ChatRequest` pour effectuer des actions courantes dans Chatterbox, telles que l'envoi d'un message, la suppression d'un message et la déconnexion d'autres utilisateurs.

Toutes les actions d'une salle de chat suivent un schéma commun : à chaque action que vous effectuez dans une salle de chat, il existe un objet de demande correspondant. Pour chaque demande, il existe un objet de réponse correspondant que vous recevez lors de la confirmation de la demande.

Tant que vos utilisateurs disposent des autorisations appropriées lorsque vous créez un jeton de chat, ils peuvent effectuer avec succès la ou les actions correspondantes à l'aide des objets de demande pour voir quelles demandes vous pouvez effectuer dans une salle de chat.

Ci-dessous, nous expliquons comment [envoyer un message](#chat-js-room-actions-sending-message) et [supprimer un message](#chat-js-room-actions-deleting-message).

### Envoi d'un message
<a name="chat-js-room-actions-sending-message"></a>

La classe `SendMessageRequest` permet d'envoyer des messages dans une salle de chat. Ici, vous modifiez votre `App` pour envoyer une demande de message à l'aide du composant que vous avez créé dans [Créer une entrée de message](chat-sdk-js-tutorial-chat-rooms.md#chat-js-rooms-message-input) (dans la première partie de ce didacticiel).

Pour commencer, définissez une nouvelle propriété booléenne nommée `isSending` avec le hook `useState`. Utilisez cette nouvelle propriété pour activer l'état désactivé de votre élément HTML `button` à l'aide de la constante `isSendDisabled`. Dans le gestionnaire d'événements correspondant à votre `SendButton`, effacez la valeur de `messageToSend` et définissez `isSending` sur true (vrai).

*Comme vous allez passer un appel d'API à partir de ce bouton, l'ajout du booléen `isSending` permet d'éviter que plusieurs appels d'API ne se produisent en même temps, en désactivant les interactions utilisateur sur votre `SendButton` jusqu'à ce que la demande soit complète.*

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

// ...

const [isSending, setIsSending] = useState(false);

// ...

const onMessageSend = () => {
  setIsSending(true);
  setMessageToSend('');
};

// ...

const isSendDisabled = connectionState !== 'connected' || isSending;

// ...
```

Préparez la demande en créant une instance `SendMessageRequest` et en transmettant le contenu du message au constructeur. Après avoir défini les états `isSending` et `messageToSend`, appelez la méthode `sendMessage` qui envoie la demande à la salle de chat. Enfin, effacez l'indicateur `isSending` lors de la réception de la confirmation ou du rejet de la demande.

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

```
// App.tsx

// ...
import { ChatMessage, 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);
  }
};

// ...
```

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

```
// App.jsx

// ...
import { ChatRoom, 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);
  }
};

// ...
```

------

Essayez Chatterbox : essayez d'envoyer un message en rédigeant un message avec votre `MessageInput` et en appuyant sur votre `SendButton`. Vous devriez voir le message que vous avez envoyé s'afficher dans la `MessageList` que vous avez créée précédemment.

### Supprimer un message
<a name="chat-js-room-actions-deleting-message"></a>

Pour supprimer un message d'une salle de chat, vous devez disposer de la capacité appropriée. Les capacités sont accordées lors de l'initialisation du jeton de chat que vous utilisez pour vous authentifier dans une salle de chat. Pour les besoins de cette section, le formulaire `ServerApp` de la section [Configurer un serveur d'authentification/d'autorisation local](chat-sdk-js-tutorial-chat-rooms.md#chat-js-rooms-auth-server) (dans la partie 1 de ce didacticiel) vous permet de spécifier les capacités des modérateurs. Cela se fait dans votre application à l'aide de l'objet `tokenProvider` que vous avez créé dans la section [Créer un fournisseur de jetons](chat-sdk-js-tutorial-chat-rooms.md#chat-js-rooms-token-provider) (également dans la partie 1).

Vous pouvez ici modifier votre `Message` en ajoutant une fonction pour supprimer le message.

Tout d'abord, ouvrez le `App.tsx` et ajoutez la fonctionnalité `DELETE_MESSAGE`. (`capabilities` est le deuxième paramètre de votre fonction `tokenProvider`.)

Remarque : c'est de cette manière que votre `ServerApp` informe les API IVS Chat que l'utilisateur associé au jeton de chat obtenu peut supprimer des messages dans une salle de chat. Dans une situation réelle, vous aurez probablement une logique backend plus complexe pour gérer les capacités des utilisateurs dans l'infrastructure de votre application serveur.

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

```
// App.tsx

// ...

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

// ...
```

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

```
// App.jsx

// ...

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

// ...
```

------

Au cours des étapes suivantes, vous mettrez à jour votre `Message` pour afficher un bouton de suppression.

Ouvrez `Message` et définissez un nouvel état booléen nommé `isDeleting` à l'aide du hook `useState` avec une valeur initiale de `false`. En utilisant cet état, mettez à jour le contenu de votre `Button` pour qu'il ait un aspect différent en fonction de l'état actuel de `isDeleting`. Désactivez votre bouton lorsque `isDeleting` est true (vrai) ; cela vous évite d'essayer de faire deux demandes de suppression de message simultanément. 

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

```
// Message.tsx

import React, { useState } from 'react';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { useUserContext } from './UserContext';

type Props = {
  message: ChatMessage;
}

export const Message = ({ message }: Props) => {
  const { userId } = useUserContext();
  const [isDeleting, setIsDeleting] = useState(false);

  const isMine = message.sender.userId === userId;

  return (
    <div style={{ backgroundColor: isMine ? 'lightblue' : 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
      <button disabled={isDeleting}>Delete</button>
    </div>
  );
};
```

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

```
// Message.jsx

import React from 'react';
import { useUserContext } from './UserContext';

export const Message = ({ message }) => {
  const { userId } = useUserContext();
  const [isDeleting, setIsDeleting] = useState(false);

  return (
    <div style={{ backgroundColor: isMine ? 'lightblue' : 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
      <button disabled={isDeleting}>Delete</button>
    </div>
  );
};
```

------

Définissez une nouvelle fonction appelée `onDelete` qui accepte une chaîne comme paramètre et renvoie `Promise`. Dans le corps de la fermeture de l'action de votre `Button`, utilisez `setIsDeleting` pour faire basculer votre booléen `isDeleting` avant et après un appel à `onDelete`. Pour le paramètre de chaîne, transmettez l'ID du message de votre composant.

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

```
// Message.tsx

import React, { useState } from 'react';
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 [isDeleting, setIsDeleting] = useState(false);
  const isMine = message.sender.userId === userId;
  const handleDelete = async () => {
    setIsDeleting(true);
    try {
      await onDelete(message.id);
    } catch (e) {
      console.log(e);
      // handle chat error here...
    } finally {
      setIsDeleting(false);
    }
  };

  return (
    <div style={{ backgroundColor: isMine ? 'lightblue' : 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{content}</p>
      <button onClick={handleDelete} disabled={isDeleting}>
        Delete
      </button>
    </div>
  );
};
```

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

```
// Message.jsx

import React, { useState } from 'react';
import { useUserContext } from './UserContext';

export const Message = ({ message, onDelete }) => {
  const { userId } = useUserContext();
  const [isDeleting, setIsDeleting] = useState(false);
  const isMine = message.sender.userId === userId;
  const handleDelete = async () => {
    setIsDeleting(true);
    try {
      await onDelete(message.id);
    } catch (e) {
      console.log(e);
      // handle the exceptions here...
    } finally {
      setIsDeleting(false);
    }
  };

  return (
    <div style={{ backgroundColor: 'silver', padding: 6, borderRadius: 10, margin: 10 }}>
      <p>{message.content}</p>
      <button onClick={handleDelete} disabled={isDeleting}>
        Delete
      </button>
    </div>
  );
};
```

------

Ensuite, mettez à jour votre composant `MessageList` pour qu'il reflète les dernières modifications apportées à votre composant `Message`.

Ouvrez `MessageList` et définissez une nouvelle fonction nommée `onDelete` qui accepte une chaîne en tant que paramètre et renvoie `Promise`. Mettez à jour votre `Message` et transmettez-le via les propriétés de `Message`. Le paramètre de chaîne de votre nouvelle fermeture sera l'identifiant du message que vous souhaitez supprimer, qui sera transmis par votre `Message`.

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

```
// MessageList.tsx

import * as React from 'react';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { Message } from './Message';

interface Props {
  messages: ChatMessage[];
  onDelete(id: string): Promise<void>;
}

export const MessageList = ({ messages, onDelete }: Props) => {
  return (
    <>
      {messages.map((message) => (
        <Message key={message.id} onDelete={onDelete} content={message.content} id={message.id} />
      ))}
    </>
  );
};
```

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

```
// MessageList.jsx

import * as React from 'react';
import { Message } from './Message';

export const MessageList = ({ messages, onDelete }) => {
  return (
    <>
      {messages.map((message) => (
        <Message key={message.id} onDelete={onDelete} content={message.content} id={message.id} />
      ))}
    </>
  );
};
```

------

Ensuite, vous mettez à jour votre `App` pour refléter les dernières modifications apportées à votre `MessageList`.

Dans `App`, définissez une fonction nommée `onDeleteMessage` et transmettez-la à la propriété `MessageList onDelete` :

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

```
// App.tsx

// ...

const onDeleteMessage = async (id: string) => {};

return (
  <div style={{ display: 'flex', flexDirection: 'column', padding: 10 }}>
    <h4>Connection State: {connectionState}</h4>
    <MessageList onDelete={onDeleteMessage} messages={messages} />
    <div style={{ flexDirection: 'row', display: 'flex', width: '100%' }}>
      <MessageInput value={messageToSend} onMessageChange={setMessageToSend} />
      <SendButton disabled={isSendDisabled} onSendPress={onMessageSend} />
    </div>
  </div>
);

// ...
```

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

```
// App.jsx

// ...

const onDeleteMessage = async (id) => {};

return (
  <div style={{ display: 'flex', flexDirection: 'column', padding: 10 }}>
    <h4>Connection State: {connectionState}</h4>
    <MessageList onDelete={onDeleteMessage} messages={messages} />
    <div style={{ flexDirection: 'row', display: 'flex', width: '100%' }}>
      <MessageInput value={messageToSend} onMessageChange={setMessageToSend} />
      <SendButton disabled={isSendDisabled} onSendPress={onMessageSend} />
    </div>
  </div>
);

// ...
```

------

Préparez une demande en créant une instance de `DeleteMessageRequest`, en transmettant l'ID de message correspondant au paramètre du constructeur et en appelant `deleteMessage` qui accepte la demande préparée ci-dessus :

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

```
// App.tsx

// ...

const onDeleteMessage = async (id: string) => {
  const request = new DeleteMessageRequest(id);
  await room.deleteMessage(request);
};

// ...
```

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

```
// App.jsx

// ...

const onDeleteMessage = async (id) => {
  const request = new DeleteMessageRequest(id);
  await room.deleteMessage(request);
};

// ...
```

------

Ensuite, vous mettez à jour votre état `messages` pour refléter une nouvelle liste de messages qui omet le message que vous venez de supprimer.

Dans le hook `useEffect`, écoutez l'événement `messageDelete` et mettez à jour votre tableau d'états `messages` en supprimant le message dont l'ID correspond au paramètre `message`.

Remarque : l'événement `messageDelete` peut être déclenché en cas de suppression de messages par l'utilisateur actuel ou par tout autre utilisateur présent dans la salle. Le gérer dans le gestionnaire d'événements (plutôt qu'à côté de la demande `deleteMessage`) vous permet d'unifier la gestion des messages de suppression.

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

// ...

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

return () => {
  // ...

  unsubscribeOnMessageDeleted();
};

// ...
```

Vous pouvez désormais supprimer des utilisateurs d'une salle de chat dans votre application de chat.

## Étapes suivantes
<a name="chat-js-messages-events-next-steps"></a>

À titre expérimental, essayez de mettre en œuvre d'autres actions dans une salle, par exemple la déconnexion d'un autre utilisateur.

# Kit SDK de messagerie client de chat IVS : didacticiel React Native, partie 1 : salles de chat
<a name="chat-sdk-react-tutorial-chat-rooms"></a>

Il s'agit de la première partie d'un didacticiel en deux volets. Vous apprendrez les bases de l'utilisation du kit SDK de messagerie client de chat Amazon IVS pour JavaScript en créant une application entièrement fonctionnelle utilisant React Native. Nous appelons l'application *Chatterbox*.

Le public cible est constitué de développeurs expérimentés qui découvrent le kit SDK de messagerie Amazon IVS Chat. Vous devez être à l'aise avec les langages de programmation TypeScript ou JavaScript et la bibliothèque React Native.

Par souci de concision, nous ferons référence au kit SDK de messagerie client Amazon IVS Chat pour JavaScript en le nommant kit SDK Chat JS.

**Remarque** : dans certains cas, les exemples de code pour JavaScript et TypeScript sont identiques et sont donc combinés.

Cette première partie du didacticiel est divisée en plusieurs sections :

1. [Configurer un serveur d'authentification/d'autorisation local](#chat-react-rooms-auth-server)

1. [Créer un projet Chatterbox](#chat-react-rooms-chatterbox)

1. [Se connecter à une salle de chat](#chat-react-rooms-connect)

1. [Créer un fournisseur de jetons](#chat-react-rooms-token-provider)

1. [Observer les mises à jour de la connexion](#chat-react-rooms-connection-state)

1. [Créer un composant de bouton d'envoi](#chat-react-rooms-send-button)

1. [Créer une entrée de message](#chat-react-rooms-message-input)

1. [Étapes suivantes](#chat-react-rooms-next-steps)

## Conditions préalables
<a name="chat-react-rooms-prerequisites"></a>
+ Familiarisez-vous avec TypeScript ou JavaScript et la bibliothèque React Native. Si vous ne connaissez pas React Native, découvrez les bases dans [Introduction à React Native](https://reactnative.dev/docs/tutorial) (langue française non garantie).
+ Lisez et assurez-vous d'avoir compris [Mise en route avec le chat Amazon IVS](getting-started-chat.md).
+ Créez un utilisateur AWS IAM avec les fonctionnalités CreateChatToken et CreateRoom définies dans une politique IAM existante. (Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md).)
+ Assurez-vous que les clés secrètes et d'accès de cet utilisateur sont stockées dans un fichier d'informations d'identification AWS. Pour obtenir des instructions, consultez le [Guide de l'utilisateur de l'interface de ligne de commande AWS](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) (en particulier les [paramètres de configuration et de fichier d'informations d'identification](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)).
+ Créez une salle de chat et enregistrez son ARN. Consultez [Mise en route avec le chat Amazon IVS](getting-started-chat.md). (Si vous n'enregistrez pas l'ARN, vous pourrez le consulter ultérieurement à l'aide de la console ou de l'API Chat.)
+ Installez l'environnement Node.js 14\$1 avec le gestionnaire de packages NPM ou Yarn.

## Configurer un serveur d'authentification/d'autorisation local
<a name="chat-react-rooms-auth-server"></a>

Votre application backend est chargée à la fois de créer des salles de chat et de générer les jetons de chat nécessaires au kit SDK Chat JS pour authentifier et autoriser vos clients à accéder à vos salles de chat. Vous devez utiliser votre propre backend, car vous ne pouvez pas stocker de manière sécurisée les clés AWS dans une application mobile ; des attaquants avertis pourraient les extraire et accéder à votre compte AWS.

Consultez la section [Create a Chat Token](getting-started-chat-auth.md) (Créer un jeton de chat) dans *Mise en route avec Amazon IVS Chat*. Comme le montre l'organigramme, votre application côté serveur est chargée de créer un jeton de chat. Cela signifie que votre application doit fournir ses propres moyens de générer un jeton de chat en demandant un jeton à votre application côté serveur.

Dans cette section, vous apprendrez les bases de la création d'un fournisseur de jetons dans votre backend. Nous utilisons l'infrastructure express pour créer un serveur local en direct qui gère la création de jetons de chat à l'aide de votre environnement AWS local.

Créez un projet `npm` vide à l'aide de NPM. Créez un répertoire pour héberger votre application et faites-en votre répertoire de travail :

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

Utilisez `npm init` pour créer un fichier `package.json` pour votre application :

```
$ npm init
```

Cette commande vous invite à fournir plusieurs informations, notamment le nom et la version de votre application. Pour l'instant, appuyez simplement sur **ENTRÉE** pour accepter les valeurs par défaut pour la plupart d'entre elles, à l'exception de la suivante :

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

Appuyez sur **ENTRÉE** pour accepter le nom de fichier par défaut `index.js` suggéré ou saisissez le nom que vous souhaitez donner au fichier principal.

Installez maintenant les dépendances obligatoires :

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

`aws-sdk` nécessite des variables d'environnement de configuration, qui se chargent automatiquement à partir d'un fichier nommé `.env` situé dans le répertoire racine. Pour le configurer, créez un fichier nommé `.env` et renseignez les informations de configuration manquantes :

```
# .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=...
```

Créons maintenant un fichier de point d'entrée dans le répertoire racine avec le nom que vous avez saisi ci-dessus dans la commande `npm init`. Dans ce cas, nous utilisons `index.js` et importons tous les packages requis :

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

Créez maintenant une nouvelle instance de `express` :

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

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

Ensuite, vous pouvez créer votre première méthode POST de point de terminaison pour le fournisseur de jetons. Prenez les paramètres requis dans le corps de la demande (`roomId`, `userId`, `capabilities` et `sessionDurationInMinutes`) :

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

Ajoutez la validation des champs obligatoires :

```
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;
  }
});
```

Après avoir préparé la méthode POST, nous intégrons `createChatToken` avec `aws-sdk` pour la fonctionnalité de base d'authentification/autorisation :

```
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 });
    }
  });
});
```

À la fin du fichier, ajoutez un écouteur de port pour votre application `express` :

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

À présent, vous pouvez exécuter le serveur à l'aide de la commande suivante depuis la racine du projet :

```
$ node index.js
```

**Conseil** : ce serveur accepte les requêtes URL sur https://localhost:3000.

## Créer un projet Chatterbox
<a name="chat-react-rooms-chatterbox"></a>

Vous créez d'abord le projet React Native appelé `chatterbox`. Exécutez cette commande : 

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

Ou vous créez un projet d'exposition avec un modèle TypeScript.

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

Vous pouvez intégrer le kit SDK de messagerie client Chat pour JS via [Node Package Manager](https://www.npmjs.com/) ou [Yarn Package Manager](https://yarnpkg.com/) :
+ Npm : `npm install amazon-ivs-chat-messaging`
+ Yarn : `yarn add amazon-ivs-chat-messaging`

## Se connecter à une salle de chat
<a name="chat-react-rooms-connect"></a>

Ici, vous créez une `ChatRoom` et vous vous y connectez à l'aide de méthodes asynchrones. La classe `ChatRoom` gère la connexion de votre utilisateur au kit SDK Chat JS. Pour vous connecter correctement à une salle de chat, vous devez fournir une instance de `ChatToken` dans votre application React. 

Accédez au fichier `App` créé dans le projet `chatterbox` par défaut et supprimez tout ce qui est renvoyé par un composant fonctionnel. Aucun code prérempli n'est nécessaire. À ce stade, notre `App` est assez vide.

**TypeScript/JavaScript**:

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

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

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

Créez une instance `ChatRoom` et transmettez-la à l'état à l'aide du hook `useState`. Cela nécessite de passer `regionOrUrl` (la région AWS dans laquelle votre salle de chat est hébergée) et `tokenProvider` (utilisé pour le flux d'authentification/autorisation backend qui est créé dans les étapes suivantes).

**Important** : vous devez utiliser la même région AWS que celle dans laquelle vous avez créé la salle dans la [Mise en route avec Amazon IVS Chat](getting-started-chat-create-room.md). L'API est un service régional AWS. Pour obtenir la liste des régions prises en charge et des points de terminaison du service HTTPS Amazon IVS Chat, consultez la page des [régions 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>;
}
```

## Créer un fournisseur de jetons
<a name="chat-react-rooms-token-provider"></a>

À l'étape suivante, nous devons créer une fonction `tokenProvider` sans paramètre requise par le constructeur `ChatRoom`. Tout d'abord, nous allons créer une fonction `fetchChatToken` qui enverra une requête POST à l'application backend que vous avez configurée dans [Configurer un serveur d'authentification/d'autorisation local](#chat-react-rooms-auth-server). Les jetons de chat contiennent les informations nécessaires au kit SDK pour établir avec succès une connexion à la salle de chat. L'API de chat utilise ces jetons comme moyen sécurisé de valider l'identité d'un utilisateur, ses capacités au sein d'une salle de chat et la durée de la session.

Dans le navigateur de projet, créez un fichier TypeScript/JavaScript nommé `fetchChatToken`. Créez une demande de récupération à l'application `backend` et renvoyez l'objet `ChatToken` de la réponse. Ajoutez les propriétés du corps de la demande nécessaires à la création d'un jeton de chat. Utilisez les règles définies pour les [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html). Ces propriétés sont documentées dans l’opération [CreateChatToken](https://docs.aws.amazon.com//ivs/latest/ChatAPIReference/API_CreateChatToken.html#API_CreateChatToken_RequestBody).

**Remarque** : l'URL que vous utilisez ici est la même que celle que votre serveur local a créée lorsque vous avez exécuté l'application 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),
  };
}
```

------

## Observer les mises à jour de la connexion
<a name="chat-react-rooms-connection-state"></a>

Réagir aux modifications de l'état de connexion d'une salle de chat est un élément essentiel de la création d'une application de chat. Commençons par nous abonner aux événements pertinents :

**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>;
}
```

Ensuite, nous devons fournir la possibilité de lire l'état de la connexion. Nous utilisons notre hook `useState` pour créer un état local dans `App` et définir l'état de connexion dans chaque écouteur.

**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>;
}
```

Après vous être abonné à l'état de connexion, affichez l'état de la connexion et connectez-vous à la salle de chat en utilisant la méthode `room.connect` indiquée dans le 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,
  }
});

// ...
```

Vous avez correctement mis en place une connexion à la salle de chat.

## Créer un composant de bouton d'envoi
<a name="chat-react-rooms-send-button"></a>

Dans cette section, vous créez un bouton d'envoi qui a une apparence différente pour chaque état de connexion. Le bouton d'envoi facilite l'envoi de messages dans une salle de chat. Il sert également d'indicateur visuel indiquant si et quand des messages peuvent être envoyés, par exemple en cas de perte de connexion ou de sessions de chat expirées.

Commencez par créer un fichier dans le répertoire `src` de votre projet Chatterbox et nommez-le `SendButton`. Ensuite, créez un composant qui affichera un bouton pour votre application de chat. Exportez votre `SendButton` et importez-le dans `App`. Dans le champ `<View></View>` vide, ajoutez `<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>
);
```

------

Ensuite, dans `App`, définissez une fonction nommée `onMessageSend` et transmettez-la à la propriété `SendButton onPress`. Définissez une autre variable nommée `isSendDisabled` (qui empêche l'envoi de messages lorsque la salle n'est pas connectée) et transmettez-la à la propriété `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>
);

// ...
```

## Créer une entrée de message
<a name="chat-react-rooms-message-input"></a>

La barre de messages Chatterbox est le composant avec lequel vous allez interagir pour envoyer des messages à une salle de chat. Elle contient généralement une entrée de texte pour rédiger votre message et un bouton pour envoyer votre message.

Pour créer un composant `MessageInput`, créez d'abord un fichier dans le répertoire `src` et nommez-le `MessageInput`. Ensuite, créez un composant d'entrée qui affichera une entrée pour votre application de chat. Exportez votre `MessageInput` et importez-le dans `App` (au-dessus du `<SendButton />`).

Créez un état nommé `messageToSend` à l'aide du hook `useState`, avec une chaîne vide comme valeur par défaut. Dans le corps de votre application, passez `messageToSend` à `value` de `MessageInput` et transmettez `setMessageToSend` à la propriété `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',
  }
});
```

------

## Étapes suivantes
<a name="chat-react-rooms-next-steps"></a>

Maintenant que vous avez fini de créer une barre de messages pour Chatterbox, passez à la seconde partie de ce didacticiel React Native, [Messages et événements](chat-sdk-react-tutorial-messages-events.md).

# Kit SDK de messagerie client de chat IVS : didacticiel React Native, partie 2 : messages et événements
<a name="chat-sdk-react-tutorial-messages-events"></a>

Cette seconde et dernière partie du didacticiel est divisée en plusieurs sections :

1. [S'abonner aux événements des messages de chat](#chat-react-messages-events-subscribe)

1. [Afficher les messages reçus](#chat-react-messages-events-show)

   1.  [Créer un composant de message](#chat-react-messages-create-component)

   1. [Reconnaître les messages envoyés par l'utilisateur actuel](#chat-react-messages-recognize)

   1. [Afficher une liste de messages de chat](#chat-react-messages-render-list)

1. [Effectuer des actions dans une salle de chat](#chat-react-messages-events-room-actions)

   1. [Envoi d'un message](#chat-react-room-actions-sending-message)

   1. [Supprimer un message](#chat-react-room-actions-deleting-message)

1. [Étapes suivantes](#chat-react-messages-events-next-steps)

**Remarque** : dans certains cas, les exemples de code pour JavaScript et TypeScript sont identiques et sont donc combinés.

## Prérequis
<a name="chat-react-messages-events-prerequisite"></a>

Assurez-vous d'avoir terminé la première partie de ce didacticiel relative aux [Salles de chat](chat-sdk-react-tutorial-chat-rooms.md).

## S'abonner aux événements des messages de chat
<a name="chat-react-messages-events-subscribe"></a>

L'instance `ChatRoom` utilise des événements pour communiquer lorsque des événements se produisent dans une salle de chat. Pour commencer à mettre en œuvre l'expérience de chat, vous devez montrer à vos utilisateurs quand d'autres personnes envoient un message dans la salle à laquelle ils sont connectés.

Ici, vous vous abonnez aux événements des messages de chat. Plus tard, nous vous montrerons comment mettre à jour une liste de messages que vous créez, qui est mise à jour à chaque message/événement.

Dans votre `App`, dans le hook `useEffect`, abonnez-vous à tous les événements de message :

**TypeScript/JavaScript** :

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

useEffect(() => {
  // ...
  const unsubscribeOnMessageReceived = room.addListener('message', (message) => {});

  return () => {
    // ...
    unsubscribeOnMessageReceived();
  };
}, []);
```

## Afficher les messages reçus
<a name="chat-react-messages-events-show"></a>

La réception de messages est au cœur de l'expérience de chat. À l'aide du kit SDK Chat JS, vous pouvez configurer votre code pour recevoir facilement les événements des autres utilisateurs connectés à une salle de chat.

Plus tard, nous vous montrerons comment effectuer des actions dans une salle de chat qui tirent parti des composants que vous créez ici.

Dans votre `App`, définissez un état nommé `messages` avec un type de tableau `ChatMessage` nommé `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([]);

  //...
}
```

------

Ensuite, dans la fonction d'écouteur `message`, ajoutez `message` au tableau `messages` :

**TypeScript/JavaScript**:

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

// ...

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

// ...
```

Ci-dessous, nous passons en revue les tâches pour afficher les messages reçus :

1.  [Créer un composant de message](#chat-react-messages-create-component)

1. [Reconnaître les messages envoyés par l'utilisateur actuel](#chat-react-messages-recognize)

1. [Afficher une liste de messages de chat](#chat-react-messages-render-list)

### Créer un composant de message
<a name="chat-react-messages-create-component"></a>

Le composant `Message` est chargé de rendre le contenu d'un message reçu par votre salle de chat. Dans cette section, vous créez un composant de messages pour afficher les messages de chat individuels dans l'`App`.

Dans le répertoire `src`, créez un fichier nommé `Message`. Transmettez le type `ChatMessage` de ce composant et transmettez la chaîne `content` provenant des propriétés `ChatMessage` pour afficher le texte du message reçu des écouteurs de messages de la salle de chat. Dans le navigateur de projets, accédez à `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,
  },
});
```

------

**Conseil** : utilisez ce composant pour stocker les différentes propriétés que vous souhaitez afficher dans les lignes de vos messages, par exemple, les URL des avatars, les noms d'utilisateur et les horodatages de l'envoi du message.

### Reconnaître les messages envoyés par l'utilisateur actuel
<a name="chat-react-messages-recognize"></a>

Pour reconnaître le message envoyé par l'utilisateur actuel, nous modifions le code et créons un contexte React pour stocker le `userId` de l'utilisateur actuel.

Dans le répertoire `src`, créez un fichier nommé `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;
```

------

Remarque : ici, nous avons utilisé le hook `useState` pour stocker la valeur `userId`. Dorénavant, vous pourrez utiliser `setUserId` pour modifier le contexte de l'utilisateur ou à des fins de connexion.

Ensuite, remplacez `userId` dans le premier paramètre transmis à `tokenProvider`, en utilisant le contexte créé précédemment. Assurez-vous d'ajouter la capacité `SEND_MESSAGE` à votre fournisseur de jetons, comme indiqué ci-dessous ; elle est requise pour envoyer des messages.

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

  // ...
}
```

------

Dans votre composant `Message`, utilisez le `UserContext` créé auparavant, déclarez la variable `isMine`, associez le `userId` de l'expéditeur au `userId` du contexte et appliquez différents styles de messages à l'utilisateur actuel.

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

------

### Afficher une liste de messages de chat
<a name="chat-react-messages-render-list"></a>

Maintenant, répertoriez les messages à l'aide des composants `FlatList` et `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>
);

// ...
```

------

Toutes les pièces du puzzle sont maintenant en place pour que votre `App` commence à afficher les messages reçus par votre salle de chat. Continuez ci-dessous pour découvrir comment réaliser des actions dans une salle de chat qui tirent parti des composants que vous avez créés.

## Effectuer des actions dans une salle de chat
<a name="chat-react-messages-events-room-actions"></a>

L'envoi de messages et l'exécution d'actions de modérateur sont quelques-uns des principaux moyens d'interagir avec une salle de chat. Vous apprendrez ici comment utiliser divers objets de demande de chat pour effectuer des actions courantes dans Chatterbox, telles que l'envoi d'un message, la suppression d'un message et la déconnexion d'autres utilisateurs.

Toutes les actions d'une salle de chat suivent un schéma commun : à chaque action que vous effectuez dans une salle de chat, il existe un objet de demande correspondant. Pour chaque demande, il existe un objet de réponse correspondant que vous recevez lors de la confirmation de la demande.

Tant que vos utilisateurs disposent des capacités appropriées lorsque vous créez un jeton de chat, ils peuvent effectuer avec succès la ou les actions correspondantes à l'aide des objets de demande pour voir quelles demandes vous pouvez effectuer dans une salle de chat.

Ci-dessous, nous expliquons comment [envoyer un message](#chat-react-room-actions-sending-message) et [supprimer un message](#chat-react-room-actions-deleting-message).

### Envoi d'un message
<a name="chat-react-room-actions-sending-message"></a>

La classe `SendMessageRequest` permet d'envoyer des messages dans une salle de chat. Ici, vous modifiez votre `App` pour envoyer une demande de message à l'aide du composant que vous avez créé dans [Créer une entrée de message](chat-sdk-react-tutorial-chat-rooms.md#chat-react-rooms-message-input) (dans la première partie de ce didacticiel).

Pour commencer, définissez une nouvelle propriété booléenne nommée `isSending` avec le hook `useState`. Utilisez cette nouvelle propriété pour activer l'état désactivé de votre élément `button` à l'aide de la constante `isSendDisabled`. Dans le gestionnaire d'événements correspondant à votre `SendButton`, effacez la valeur de `messageToSend` et définissez `isSending` sur true (vrai).

*Comme vous allez passer un appel d'API à partir de ce bouton, l'ajout du booléen `isSending` permet d'éviter que plusieurs appels d'API ne se produisent en même temps, en désactivant les interactions utilisateur sur votre `SendButton` jusqu'à ce que la demande soit complète.*

Remarque : l'envoi de messages ne fonctionne que si vous avez ajouté la capacité `SEND_MESSAGE` à votre fournisseur de jetons, comme indiqué ci-dessus dans la rubrique [Reconnaître les messages envoyés par l'utilisateur actuel](#chat-react-messages-recognize).

**TypeScript/JavaScript**:

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

// ...

const [isSending, setIsSending] = useState(false);

// ...

const onMessageSend = () => {
  setIsSending(true);
  setMessageToSend('');
};

// ...

const isSendDisabled = connectionState !== 'connected' || isSending;

// ...
```

Préparez la demande en créant une instance `SendMessageRequest` et en transmettant le contenu du message au constructeur. Après avoir défini les états `isSending` et `messageToSend`, appelez la méthode `sendMessage` qui envoie la demande à la salle de chat. Enfin, effacez l'indicateur `isSending` lors de la réception de la confirmation ou du rejet de la demande.

**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);
  }
};

// ...
```

Essayez Chatterbox : essayez d'envoyer un message en rédigeant un message avec votre `MessageBar` et en appuyant sur votre `SendButton`. Vous devriez voir le message que vous avez envoyé s'afficher dans la `MessageList` que vous avez créée précédemment.

### Supprimer un message
<a name="chat-react-room-actions-deleting-message"></a>

Pour supprimer un message d'une salle de chat, vous devez disposer de la capacité appropriée. Les capacités sont accordées lors de l'initialisation du jeton de chat que vous utilisez pour vous authentifier dans une salle de chat. Pour les besoins de cette section, le formulaire `ServerApp` de la section [Configurer un serveur d'authentification/d'autorisation local](chat-sdk-react-tutorial-chat-rooms.md#chat-react-rooms-auth-server) (dans la partie 1 de ce didacticiel) vous permet de spécifier les capacités des modérateurs. Cela se fait dans votre application à l'aide de l'objet `tokenProvider` que vous avez créé dans la section [Créer un fournisseur de jetons](chat-sdk-react-tutorial-chat-rooms.md#chat-react-rooms-token-provider) (également dans la partie 1).

Vous pouvez ici modifier votre `Message` en ajoutant une fonction pour supprimer le message.

Tout d'abord, ouvrez le `App.tsx` et ajoutez la fonctionnalité `DELETE_MESSAGE`. (`capabilities` est le deuxième paramètre de votre fonction `tokenProvider`.)

Remarque : c'est de cette manière que votre `ServerApp` informe les API IVS Chat que l'utilisateur associé au jeton de chat obtenu peut supprimer des messages dans une salle de chat. Dans une situation réelle, vous aurez probablement une logique backend plus complexe pour gérer les capacités des utilisateurs dans l'infrastructure de votre application serveur.

**TypeScript/JavaScript**:

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

// ...

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

// ...
```

Au cours des étapes suivantes, vous mettrez à jour votre `Message` pour afficher un bouton de suppression.

Définissez une nouvelle fonction appelée `onDelete` qui accepte une chaîne comme paramètre et renvoie `Promise`. Pour le paramètre de chaîne, transmettez l'ID du message de votre composant.

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

------

Ensuite, mettez à jour votre composant `renderItem` pour qu'il reflète les dernières modifications apportées à votre composant `FlatList`.

Dans `App`, définissez une fonction nommée `handleDeleteMessage` et transmettez-la à la propriété `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]);

// ...
```

------

Préparez une demande en créant une instance de `DeleteMessageRequest`, en transmettant l'ID de message correspondant au paramètre du constructeur et en appelant `deleteMessage` qui accepte la demande préparée ci-dessus :

------
#### [ 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);
};

// ...
```

------

Ensuite, vous mettez à jour votre état `messages` pour refléter une nouvelle liste de messages qui omet le message que vous venez de supprimer.

Dans le hook `useEffect`, écoutez l'événement `messageDelete` et mettez à jour votre tableau d'états `messages` en supprimant le message dont l'ID correspond au paramètre `message`.

Remarque : l'événement `messageDelete` peut être déclenché en cas de suppression de messages par l'utilisateur actuel ou par tout autre utilisateur présent dans la salle. Le gérer dans le gestionnaire d'événements (plutôt qu'à côté de la demande `deleteMessage`) vous permet d'unifier la gestion des messages de suppression.

**TypeScript/JavaScript**:

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

// ...

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

return () => {
  // ...

  unsubscribeOnMessageDeleted();
};

// ...
```

Vous pouvez désormais supprimer des utilisateurs d'une salle de chat dans votre application de chat.

## Étapes suivantes
<a name="chat-react-messages-events-next-steps"></a>

À titre expérimental, essayez de mettre en œuvre d'autres actions dans une salle, par exemple la déconnexion d'un autre utilisateur.

# Kit SDK de messagerie client de chat IVS : bonnes pratiques React et React Native
<a name="chat-sdk-react-best-practices"></a>

Ce document décrit les pratiques les plus importantes relatives à l'utilisation du kit SDK de messagerie de chat Amazon IVS pour React et React Native. Ces informations devraient vous permettre de créer des fonctionnalités de chat classiques dans une application React et vous fournir les informations de base dont vous avez besoin pour approfondir les parties les plus avancées du kit SDK de messagerie Chat IVS.

## Création d'un hook d'initialisation ChatRoom
<a name="chatroom-initializer-hook"></a>

La classe `ChatRoom` contient les méthodes de chat de base et les écouteurs permettant de gérer l'état de la connexion et d'écouter les événements tels que les messages reçus et supprimés. Ici, nous montrons comment stocker correctement les instances de chat dans un hook.

### Mise en œuvre
<a name="chatroom-initializer-hook-implementation"></a>

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

```
// useChatRoom.ts

import React from 'react';
import { ChatRoom, ChatRoomConfig } from 'amazon-ivs-chat-messaging';

export const useChatRoom = (config: ChatRoomConfig) => {
  const [room] = React.useState(() => new ChatRoom(config));

  return { room };
};
```

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

```
import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';

export const useChatRoom = (config) => {
  const [room] = React.useState(() => new ChatRoom(config));

  return { room };
};
```

------

Remarque : nous n'utilisons pas la méthode `dispatch` à partir du hook `setState`, car vous ne pouvez pas mettre à jour les paramètres de configuration à la volée. Le kit SDK crée une instance une seule fois et il n'est pas possible de mettre à jour le fournisseur de jetons.

**Important** : utilisez le hook d'initialisation `ChatRoom` une seule fois pour initialiser une nouvelle instance de salle de chat.

### Exemple
<a name="chatroom-initializer-hook-example"></a>

**TypeScript/JavaScript**:

```
// ...

const MyChatScreen = () => {
  const userId = 'Mike';
  const { room } = useChatRoom({
    regionOrUrl: SOCKET_URL,
    tokenProvider: () => tokenProvider(ROOM_ID, ['SEND_MESSAGE']),
  });

  const handleConnect = () => {
    room.connect();
  };

  // ...
};

// ...
```

### Écoute de l'état de la connexion
<a name="chatroom-initializer-hook-connection-state"></a>

Vous pouvez éventuellement vous abonner aux mises à jour de l'état de connexion dans votre hook de salle de chat.

#### Mise en œuvre
<a name="connection-state-implementation"></a>

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

```
// useChatRoom.ts

import React from 'react';
import { ChatRoom, ChatRoomConfig, ConnectionState } from 'amazon-ivs-chat-messaging';

export const useChatRoom = (config: ChatRoomConfig) => {
  const [room] = useState(() => new ChatRoom(config));

  const [state, setState] = React.useState<ConnectionState>('disconnected');

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

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

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

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

  return { room, state };
};
```

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

```
// useChatRoom.js

import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';

export const useChatRoom = (config) => {
  const [room] = useState(() => new ChatRoom(config));

  const [state, setState] = React.useState('disconnected');

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

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

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

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

  return { room, state };
};
```

------

## Fournisseur d'instances ChatRoom
<a name="chatroom-instance-provider"></a>

Pour utiliser le hook dans d'autres composants (afin d'éviter le prop drilling), vous pouvez créer un fournisseur de salle de chat à l'aide de `context` de React.

### Mise en œuvre
<a name="chatroom-instance-provider-implementation"></a>

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

```
// ChatRoomContext.tsx

import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';

const ChatRoomContext = React.createContext<ChatRoom | undefined>(undefined);

export const useChatRoomContext = () => {
  const context = React.useContext(ChatRoomContext);

  if (context === undefined) {
    throw new Error('useChatRoomContext must be within ChatRoomProvider');
  }

  return context;
};

export const ChatRoomProvider = ChatRoomContext.Provider;
```

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

```
// ChatRoomContext.jsx

import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';

const ChatRoomContext = React.createContext(undefined);

export const useChatRoomContext = () => {
  const context = React.useContext(ChatRoomContext);

  if (context === undefined) {
    throw new Error('useChatRoomContext must be within ChatRoomProvider');
  }

  return context;
};

export const ChatRoomProvider = ChatRoomContext.Provider;
```

------

### Exemple
<a name="chatroom-instance-provider-example"></a>

Après la création de `ChatRoomProvider`, vous pouvez utiliser votre instance avec `useChatRoomContext`.

**Important** : placez le fournisseur au niveau racine uniquement si vous avez besoin d'accéder au `context` situé entre l'écran de chat et les autres composants au milieu, afin d'éviter des rendus inutiles si vous êtes à l'écoute des connexions. Sinon, placez le fournisseur le plus près possible de l'écran de chat.

**TypeScript/JavaScript**:

```
// AppContainer

const AppContainer = () => {
  const { room } = useChatRoom({
    regionOrUrl: SOCKET_URL,
    tokenProvider: () => tokenProvider(ROOM_ID, ['SEND_MESSAGE']),
  });

  return (
    <ChatRoomProvider value={room}>
      <MyChatScreen />
    </ChatRoomProvider>
  );
};

// MyChatScreen

const MyChatScreen = () => {
  const room = useChatRoomContext();

  const handleConnect = () => {
    room.connect();
  };
  // ...
};

// ...
```

## Création d'un écouteur de messages
<a name="message-listener"></a>

Pour rester au courant de tous les messages entrants, vous devez vous abonner aux événements `message` et `deleteMessage`. Voici un exemple de code qui fournit des messages de chat pour vos composants.

**Important** : pour des raisons de performances, nous séparons `ChatMessageContext` de `ChatRoomProvider`, car nous pouvons obtenir de nombreux nouveaux rendus lorsque l'écouteur des messages de chat met à jour l'état de son message. N'oubliez pas d'appliquer `ChatMessageContext` dans les composants où vous utiliserez `ChatMessageProvider`.

### Mise en œuvre
<a name="message-listener-implementation"></a>

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

```
// ChatMessagesContext.tsx

import React from 'react';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { useChatRoomContext } from './ChatRoomContext';

const ChatMessagesContext = React.createContext<ChatMessage[] | undefined>(undefined);

export const useChatMessagesContext = () => {
  const context = React.useContext(ChatMessagesContext);

  if (context === undefined) {
    throw new Error('useChatMessagesContext must be within ChatMessagesProvider);
  }

  return context;
};

export const ChatMessagesProvider = ({ children }: { children: React.ReactNode }) => {
  const room = useChatRoomContext();

  const [messages, setMessages] = React.useState<ChatMessage[]>([]);

  React.useEffect(() => {
    const unsubscribeOnMessageReceived = room.addListener('message', (message) => {
      setMessages((msgs) => [message, ...msgs]);
    });

    const unsubscribeOnMessageDeleted = room.addListener('messageDelete', (deleteEvent) => {
      setMessages((prev) => prev.filter((message) => message.id !== deleteEvent.messageId));
    });

    return () => {
      unsubscribeOnMessageDeleted();
      unsubscribeOnMessageReceived();
    };
  }, [room]);

  return <ChatMessagesContext.Provider value={messages}>{children}</ChatMessagesContext.Provider>;
};
```

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

```
// ChatMessagesContext.jsx

import React from 'react';
import { useChatRoomContext } from './ChatRoomContext';

const ChatMessagesContext = React.createContext(undefined);

export const useChatMessagesContext = () => {
  const context = React.useContext(ChatMessagesContext);

  if (context === undefined) {
    throw new Error('useChatMessagesContext must be within ChatMessagesProvider);
  }

  return context;
};

export const ChatMessagesProvider = ({ children }) => {
  const room = useChatRoomContext();

  const [messages, setMessages] = React.useState([]);

  React.useEffect(() => {
    const unsubscribeOnMessageReceived = room.addListener('message', (message) => {
      setMessages((msgs) => [message, ...msgs]);
    });

    const unsubscribeOnMessageDeleted = room.addListener('messageDelete', (deleteEvent) => {
      setMessages((prev) => prev.filter((message) => message.id !== deleteEvent.messageId));
    });

    return () => {
      unsubscribeOnMessageDeleted();
      unsubscribeOnMessageReceived();
    };
  }, [room]);

  return <ChatMessagesContext.Provider value={messages}>{children}</ChatMessagesContext.Provider>;
};
```

------

### Exemple dans React
<a name="message-listener-example-react"></a>

**Important** : n'oubliez pas d'envelopper le conteneur de votre message avec `ChatMessagesProvider`. La ligne `Message` est un exemple de composant qui affiche le contenu d'un message.

**TypeScript/JavaScript**:

```
// your message list component...

import React from 'react';
import { useChatMessagesContext } from './ChatMessagesContext';

const MessageListContainer = () => {
  const messages = useChatMessagesContext();

  return (
    <React.Fragment>
      {messages.map((message) => (
        <MessageRow message={message} />
      ))}
    </React.Fragment>
  );
};
```

### Exemple dans React Native
<a name="message-listener-example-react-native"></a>

Par défaut, `ChatMessage` contient `id`, qui est utilisé automatiquement comme clés React dans `FlatList` pour chaque ligne ; vous n'avez donc pas besoin de transmettre `keyExtractor`.

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

```
// MessageListContainer.tsx

import React from 'react';
import { ListRenderItemInfo, FlatList } from 'react-native';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { useChatMessagesContext } from './ChatMessagesContext';

const MessageListContainer = () => {
  const messages = useChatMessagesContext();

  const renderItem = useCallback(({ item }: ListRenderItemInfo<ChatMessage>) => <MessageRow />, []);

  return <FlatList data={messages} renderItem={renderItem} />;
};
```

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

```
// MessageListContainer.jsx

import React from 'react';
import { FlatList } from 'react-native';
import { useChatMessagesContext } from './ChatMessagesContext';

const MessageListContainer = () => {
  const messages = useChatMessagesContext();

  const renderItem = useCallback(({ item }) => <MessageRow />, []);

  return <FlatList data={messages} renderItem={renderItem} />;
};
```

------

## Plusieurs instances de salle de chat dans une application
<a name="multiple-chatroom-instances"></a>

Si vous utilisez plusieurs salles de chat simultanées dans votre application, nous vous proposons de créer chaque fournisseur pour chaque chat et de l'utiliser dans le fournisseur de chat. Dans cet exemple, nous créons un chat de bot d'assistance et d'aide client. Nous créons un fournisseur pour les deux.

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

```
// SupportChatProvider.tsx

import React from 'react';
import { SUPPORT_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';

export const SupportChatProvider = ({ children }: { children: React.ReactNode }) => {
  const { room } = useChatRoom({
    regionOrUrl: SOCKET_URL,
    tokenProvider: () => tokenProvider(SUPPORT_ROOM_ID, ['SEND_MESSAGE']),
  });

  return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};

// SalesChatProvider.tsx

import React from 'react';
import { SALES_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';

export const SalesChatProvider = ({ children }: { children: React.ReactNode }) => {
  const { room } = useChatRoom({
    regionOrUrl: SOCKET_URL,
    tokenProvider: () => tokenProvider(SALES_ROOM_ID, ['SEND_MESSAGE']),
  });

  return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};
```

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

```
// SupportChatProvider.jsx

import React from 'react';
import { SUPPORT_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';

export const SupportChatProvider = ({ children }) => {
  const { room } = useChatRoom({
    regionOrUrl: SOCKET_URL,
    tokenProvider: () => tokenProvider(SUPPORT_ROOM_ID, ['SEND_MESSAGE']),
  });

  return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};

// SalesChatProvider.jsx

import React from 'react';
import { SALES_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';

export const SalesChatProvider = ({ children }) => {
  const { room } = useChatRoom({
    regionOrUrl: SOCKET_URL,
    tokenProvider: () => tokenProvider(SALES_ROOM_ID, ['SEND_MESSAGE']),
  });

  return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};
```

------

### Exemple dans React
<a name="multiple-chatroom-instances-example-react"></a>

Vous pouvez désormais utiliser différents fournisseurs de chat qui utilisent le même `ChatRoomProvider`. Plus tard, vous pourrez réutiliser le même `useChatRoomContext` dans chaque écran/affichage.

**TypeScript/JavaScript**:

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

const App = () => {
  return (
    <Routes>
      <Route
        element={
          <SupportChatProvider>
            <SupportChatScreen />
          </SupportChatProvider>
        }
      />
      <Route
        element={
          <SalesChatProvider>
            <SalesChatScreen />
          </SalesChatProvider>
        }
      />
    </Routes>
  );
};
```

### Exemple dans React Native
<a name="multiple-chatroom-instances-example-react-native"></a>

**TypeScript/JavaScript**:

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

const App = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen name="SupportChat">
        <SupportChatProvider>
          <SupportChatScreen />
        </SupportChatProvider>
      </Stack.Screen>
      <Stack.Screen name="SalesChat">
        <SalesChatProvider>
          <SalesChatScreen />
        </SalesChatProvider>
      </Stack.Screen>
    </Stack.Navigator>
  );
};
```

**TypeScript/JavaScript**:

```
// SupportChatScreen.tsx / SupportChatScreen.jsx

// ...

const SupportChatScreen = () => {
  const room = useChatRoomContext();

  const handleConnect = () => {
    room.connect();
  };

  return (
    <>
      <Button title="Connect" onPress={handleConnect} />
      <MessageListContainer />
    </>
  );
};

// SalesChatScreen.tsx / SalesChatScreen.jsx

// ...

const SalesChatScreen = () => {
  const room = useChatRoomContext();

  const handleConnect = () => {
    room.connect();
  };

  return (
    <>
      <Button title="Connect" onPress={handleConnect} />
      <MessageListContainer />
    </>
  );
};
```