Guide pratique pour utiliser les fonctionnalités d’audience dans l’infrastructure Fluid

Dans ce tutoriel, vous allez découvrir comment utiliser l’infrastructure Fluid Audience avec React pour créer une démonstration visuelle des utilisateurs qui se connectent à un conteneur. L’objet d’audience contient des informations relatives à tous les utilisateurs connectés au conteneur. Dans cet exemple, la bibliothèque cliente Azure est utilisée pour créer le conteneur et l’audience.

L’image suivante montre des boutons d’ID et un champ d’entrée d’ID de conteneur. En laissant le champ d’ID de conteneur vide et en cliquant sur un bouton d’ID utilisateur, vous créez un conteneur et le rejoignez en tant qu’utilisateur sélectionné. L’utilisateur final peut également entrer un ID de conteneur et choisir un ID utilisateur pour joindre un conteneur existant en tant qu’utilisateur sélectionné.

A screenshot of a browser with buttons for selecting a user.

L’image suivante montre plusieurs utilisateurs connectés à un conteneur représenté par des zones. La zone en bleu représente l’utilisateur qui affiche le client, tandis que les zones en noir représentent les autres utilisateurs connectés. À mesure que de nouveaux utilisateurs s’attachent au conteneur avec des ID uniques, le nombre de zones augmente.

A screenshot of a browser showing information for four different container users.

Remarque

Ce tutoriel part du principe que vous êtes familiarisé avec la Vue d’ensemble de l’infrastructure Fluid et que vous avez terminé le démarrage rapide. Vous devez également être familiarisé avec les principes de base de React, la création de projets React et les hooks React.

Créer le projet

  1. Ouvrez une invite de commandes et accédez au dossier parent dans lequel vous souhaitez créer le projet. Par exemple, C:\My Fluid Projects.

  2. Exécutez la commande suivante à l’invite. (Notez que l’interface CLI est npx, et non npm. Elle a été installée lorsque vous avez installé Node.js.)

    npx create-react-app fluid-audience-tutorial
    
  3. Le projet est créé dans un sous-dossier nommé fluid-audience-tutorial. Accédez-y avec la commande cd fluid-audience-tutorial.

  4. Le projet utilise les bibliothèques Fluid suivantes :

    Bibliothèque Description
    fluid-framework Contient la structure de données distribuées SharedMap qui synchronise les données entre les clients.
    @fluidframework/azure-client Définit la connexion à un serveur de service Fluid et définit le schéma de départ pour le conteneur Fluid.
    @fluidframework/test-client-utils Définit l’élément InsecureTokenProvider nécessaire pour créer la connexion à un service Fluid.

    Exécutez la commande suivante pour installer les bibliothèques.

    npm install @fluidframework/azure-client @fluidframework/test-client-utils fluid-framework
    

Coder le projet

Configurer les variables d’état et la vue des composants

  1. Ouvrez le fichier \src\App.js dans l’éditeur de code. Supprimez toutes les instructions import par défaut. Supprimez ensuite tout le balisage de l’instruction return. Ajoutez ensuite des instructions d’importation pour les composants et hooks React. Notez que nous allons implémenter les composants AudienceDisplay et UserIdSelection importés dans les étapes ultérieures. Le fichier doit se présenter comme suit :

        import { useState, useCallback } from "react";
        import { AudienceDisplay } from "./AudienceDisplay";
        import { UserIdSelection } from "./UserIdSelection";
    
        export const App = () => {
        // TODO 1: Define state variables to handle view changes and user input
        return (
        // TODO 2: Return view components
        );
        }
    
  2. Remplacez TODO 1 par le code suivant. Ce code initialise les variables d’état locales qui seront utilisées dans l’application. La valeur displayAudience détermine si nous restituons le composant AudienceDisplay ou le composant UserIdSelection (voir TODO 2). La valeur userId est l’identificateur utilisateur avec lequel se connecter au conteneur et la valeur containerId est le conteneur à charger. Les fonctions handleSelectUser et handleContainerNotFound sont passées en tant que rappels aux deux vues et gèrent les transitions d’état. handleSelectUser est appelé lors de la tentative de création/chargement d’un conteneur. handleContainerNotFound est appelé en cas d’échec de la création/du chargement d’un conteneur.

    Notez que les valeurs userId et containerId proviennent d’un composant UserIdSelection via la fonction handleSelectUser.

        const [displayAudience, setDisplayAudience] = useState(false);
        const [userId, setUserId] = useState();
        const [containerId, setContainerId] = useState();
    
        const handleSelectUser = useCallback((userId, containerId) => {
        setDisplayAudience(true)
        setUserId(userId);
        setContainerId(containerId);
        }, [displayAudience, userId, containerId]);
    
        const handleContainerNotFound = useCallback(() => {
        setDisplayAudience(false)
        }, [setDisplayAudience]);
    
  3. Remplacez TODO 2 par le code suivant. Comme indiqué ci-dessus, la variable displayAudience détermine si nous restituons le composant AudienceDisplay ou le composant UserIdSelection. En outre, les fonctions permettant de mettre à jour les variables d’état sont passées aux composants en tant que propriétés.

        (displayAudience) ?
        <AudienceDisplay userId={userId} containerId={containerId} onContainerNotFound={handleContainerNotFound}/> :
        <UserIdSelection onSelectUser={handleSelectUser}/>
    

Configurer le composant AudienceDisplay

  1. Créez et ouvrez un fichier \src\AudienceDisplay.js dans l’éditeur de code. Ajoutez les instructions import suivantes :

        import { useEffect, useState } from "react";
        import { SharedMap } from "fluid-framework";
        import { AzureClient } from "@fluidframework/azure-client";
        import { InsecureTokenProvider } from "@fluidframework/test-client-utils";
    

    Notez que les objets importés à partir de la bibliothèque de l’infrastructure Fluid sont requis pour définir des utilisateurs et des conteneurs. Dans les étapes suivantes, AzureClient et InsecureTokenProvider seront utilisés pour configurer le service client (voir TODO 1) tandis que SharedMap sera utilisé pour configurer un containerSchema nécessaire pour créer un conteneur (voir TODO 2).

  2. Ajoutez les composants fonctionnels et les fonctions d’assistance suivants :

        const tryGetAudienceObject = async (userId, userName, containerId) => {
        // TODO 1: Create container and return audience object
        }
    
        export const AudienceDisplay = (props) => {
        //TODO 2: Configure user ID, user name, and state variables
        //TODO 3: Set state variables and set event listener on component mount
        //TODO 4: Return list view
        }
    
        const AudienceList = (data) => {
        //TODO 5: Append view elements to list array for each member
        //TODO 6: Return list of member elements
        }
    

    Notez que AudienceDisplay et AudienceList sont des composants fonctionnels qui gèrent l’obtention et le rendu des données d’audience, tandis que la méthode tryGetAudienceObject gère la création de services de conteneur et d’audience.

Obtention d’un conteneur et d’une audience

Vous pouvez utiliser une fonction d’assistance pour obtenir les données Fluid de l’objet Audience dans la couche d’affichage (état React). La méthode tryGetAudienceObject est appelée lorsque le composant d’affichage se charge après la sélection d’un ID utilisateur. La valeur retournée est affectée à une propriété d’état React.

  1. Remplacez TODO 1 par le code suivant. Notez que les valeurs de userIduserNamecontainerId seront transmises à partir du composant Application. S’il n’y a pas containerId, un nouveau conteneur est créé. Notez également que est containerId stocké sur le hachage d’URL. Un utilisateur qui entre dans une session à partir d’un nouveau navigateur peut copier l’URL à partir d’un navigateur de session existant ou accéder à localhost:3000 l’ID de conteneur et entrer manuellement. Avec cette implémentation, nous voulons encapsuler l’appel getContainer dans un test catch dans le cas où l’utilisateur entre un ID de conteneur qui n’existe pas. Pour plus d’informations, consultez la documentation conteneurs .

        const userConfig = {
            id: userId,
            name: userName,
            additionalDetails: {
                email: userName.replace(/\s/g, "") + "@example.com",
                date: new Date().toLocaleDateString("en-US"),
            },
        };
    
        const serviceConfig = {
            connection: {
                type: "local",
                tokenProvider: new InsecureTokenProvider("", userConfig),
                endpoint: "http://localhost:7070",
            },
        };
    
        const client = new AzureClient(serviceConfig);
    
        const containerSchema = {
            initialObjects: { myMap: SharedMap },
        };
    
        let container;
        let services;
        if (!containerId) {
            ({ container, services } = await client.createContainer(containerSchema));
            const id = await container.attach();
            location.hash = id;
        } else {
            try {
                ({ container, services } = await client.getContainer(containerId, containerSchema));
            } catch (e) {
                return;
            }
        }
        return services.audience;
    

Obtention de l’audience sur le montage du composant

Maintenant que nous avons défini comment obtenir l’audience Fluid, nous devons indiquer à React d’appeler tryGetAudienceObject quand le composant Affichage d’audience est monté.

  1. Remplacez TODO 2 par le code suivant. Notez que l’ID utilisateur provient du composant parent sous la forme user1user2 ou random. Si l’ID est random, nous utilisons Math.random() pour générer un nombre aléatoire comme ID. En outre, un nom est mappé à l’utilisateur en fonction de son ID, comme spécifié dans userNameList. Enfin, nous définissons les variables d’état qui stockent les membres connectés ainsi que l’utilisateur actuel. fluidMembers stocke la liste de tous les membres connectés au conteneur, tandis que currentMember contient l’objet membre représentant l’utilisateur actuel qui affiche le contexte du navigateur.

        const userId = props.userId == "random" ? Math.random() : props.userId;
        const userNameList = {
        "user1" : "User One",
        "user2" : "User Two",
        "random" : "Random User"
        };
        const userName = userNameList[props.userId];
    
        const [fluidMembers, setFluidMembers] = useState();
        const [currentMember, setCurrentMember] = useState();
    
  2. Remplacez TODO 3 par le code suivant. Cette opération appelle tryGetAudienceObject lorsque le composant est monté, et définit les membres d’audience retournés sur fluidMembers et currentMember. Notez que nous case activée si un objet d’audience est retourné au cas où un utilisateur entre un containerId qui n’existe pas et nous devons les renvoyer à la vue UserIdSelection (props.onContainerNotFound()gérera le changement de vue). En outre, il est recommandé d’annuler l’inscription des gestionnaires d’événements lorsque le composant React se démonte en retournant audience.off.

        useEffect(() => {
        tryGetAudienceObject(userId, userName, props.containerId).then(audience => {
            if(!audience) {
            props.onContainerNotFound();
            alert("error: container id not found.");
            return;
            }
    
            const updateMembers = () => {
            setFluidMembers(audience.getMembers());
            setCurrentMember(audience.getMyself());
            }
    
            updateMembers();
    
            audience.on("membersChanged", updateMembers);
    
            return () => { audience.off("membersChanged", updateMembers) };
        });
        }, []);
    
  3. Remplacez TODO 4 par le code suivant. Notez que si fluidMembers ou currentMember n’a pas été initialisé, un écran vide est affiché. Le composant AudienceList affiche les données des membres avec un style (à implémenter dans la section suivante).

        if (!fluidMembers || !currentMember) return (<div/>);
    
        return (
            <AudienceList fluidMembers={fluidMembers} currentMember={currentMember}/>
        )
    

    Remarque

    Les transitions de connexion peuvent entraîner des fenêtres courtes où getMyself retourne undefined. Cela est dû au fait que la connexion cliente actuelle n’a pas encore été ajoutée à l’audience, de sorte qu’un ID de connexion correspondant est introuvable. Pour empêcher React de rendre une page sans membres d’audience, nous ajoutons un écouteur pour appeler updateMembers sur membersChanged. Cela fonctionne, car l’audience du service émet un événement membersChanged lorsque le conteneur est connecté.

Créer l’affichage

  1. Remplacez TODO 5 par le code suivant. Notez que nous exécutons un composant de liste pour chaque membre passé à partir du composant AudienceDisplay. Pour chaque membre, nous comparons d’abord member.userId à currentMember.userId pour vérifier si ce membre isSelf. De cette façon, nous pouvons différencier l’utilisateur client des autres utilisateurs et afficher le composant avec une couleur différente. Nous envoyons ensuite le composant de liste à un tableau list. Chaque composant affiche les données membres, comme userIduserName et additionalDetails.

        const currentMember = data.currentMember;
        const fluidMembers = data.fluidMembers;
    
        const list = [];
        fluidMembers.forEach((member, key) => {
            const isSelf = (member.userId === currentMember.userId);
            const outlineColor = isSelf ? 'blue' : 'black';
    
            list.push(
            <div style={{
                padding: '1rem',
                margin: '1rem',
                display: 'flex',
                outline: 'solid',
                flexDirection: 'column',
                maxWidth: '25%',
                outlineColor
            }} key={key}>
                <div style={{fontWeight: 'bold'}}>Name</div>
                <div>
                    {member.userName}
                </div>
                <div style={{fontWeight: 'bold'}}>ID</div>
                <div>
                    {member.userId}
                </div>
                <div style={{fontWeight: 'bold'}}>Connections</div>
                {
                    member.connections.map((data, key) => {
                        return (<div key={key}>{data.id}</div>);
                    })
                }
                <div style={{fontWeight: 'bold'}}>Additional Details</div>
                { JSON.stringify(member.additionalDetails, null, '\t') }
            </div>
            );
        });
    
  2. Remplacez TODO 6 par le code suivant. Cela rendra tous les éléments membres que nous avons poussés dans le tableau list.

        return (
            <div>
                {list}
            </div>
        );
    

Configurer le composant UserIdSelection

  1. Créez et ouvrez un fichier \src\UserIdSelection.js dans l’éditeur de code. Ce composant inclut des boutons d’ID utilisateur et des champs d’entrée d’ID de conteneur qui permettent aux utilisateurs finaux de choisir leur ID utilisateur et leur session collaborative. Ajoutez les instructions import et les composants fonctionnels suivants :

    import { useState } from 'react';
    
    export const UserIdSelection = (props) => {
        // TODO 1: Define styles and handle user inputs
        return (
        // TODO 2: Return view components
        );
    }
    
  2. Remplacez TODO 1 par le code suivant. Notez que la fonction onSelectUser met à jour les variables d’état dans le composant Application parent et demande une modification de vue. La méthode handleSubmit est déclenchée par des éléments de bouton qui seront implémentés dans TODO 2. En outre, la méthode handleChange est utilisée pour mettre à jour la variable d’état containerId. Cette méthode est appelée à partir d’un écouteur d’événement d’élément d’entrée implémenté dans TODO 2. Notez également que nous mettons à jour le containerId avec la valeur à partir d’un élément HTML avec l’ID containerIdInput (défini dans TODO 2).

        const selectionStyle = {
        marginTop: '2rem',
        marginRight: '2rem',
        width: '150px',
        height: '30px',
        };
    
        const [containerId, setContainerId] = (location.hash.substring(1));
    
        const handleSubmit = (userId) => {
        props.onSelectUser(userId, containerId);
        }
    
        const handleChange = () => {
        setContainerId(document.getElementById("containerIdInput").value);
        };
    
  3. Remplacez TODO 2 par le code suivant. Cela permet d’afficher les boutons d’ID utilisateur ainsi que le champ d’entrée d’ID de conteneur.

        <div style={{display: 'flex', flexDirection:'column'}}>
        <div style={{marginBottom: '2rem'}}>
            Enter Container Id:
            <input type="text" id="containerIdInput" value={containerId} onChange={() => handleChange()} style={{marginLeft: '2rem'}}></input>
        </div>
        {
            (containerId) ?
            (<div style={{}}>Select a User to join container ID: {containerId} as the user</div>)
            : (<div style={{}}>Select a User to create a new container and join as the selected user</div>)
        }
        <nav>
            <button type="submit" style={selectionStyle} onClick={() => handleSubmit("user1")}>User 1</button>
            <button type="submit" style={selectionStyle} onClick={() => handleSubmit("user2")}>User 2</button>
            <button type="submit" style={selectionStyle} onClick={() => handleSubmit("random")}>Random User</button>
        </nav>
        </div>
    

Démarrer le serveur Fluid et exécuter l’application

Remarque

Pour correspondre au reste de cette procédure, cette section utilise les commandes npx et npm pour démarrer un serveur Fluid. Toutefois, le code de cet article peut également s’exécuter sur un serveur Relais Azure Fluid. Pour plus d’informations, consultez Guide pratique : Provisionner un service Relais Azure Fluid et Guide pratique : Se connecter à un service Relais Azure Fluid

Dans l’invite de commandes, exécutez la commande suivante pour démarrer le service Fluid.

npx @fluidframework/azure-local-service@latest

Ouvrez une nouvelle invite de commandes et accédez à la racine du projet. par exemple C:/My Fluid Projects/fluid-audience-tutorial. Démarrez le serveur d’application à l’aide de la commande suivante. L’application s’ouvre dans le navigateur. Cela peut prendre quelques minutes.

npm run start

Accédez à localhost:3000 sur un onglet de navigateur pour afficher l’application en cours d’exécution. Pour créer un conteneur, sélectionnez un bouton d’ID utilisateur tout en laissant l’entrée d’ID de conteneur vide. Pour simuler un nouvel utilisateur qui rejoint la session de conteneur, ouvrez un nouvel onglet de navigateur et accédez à localhost:3000. Cette fois, entrez la valeur de l’ID de conteneur, qui se trouve dans l’URL du premier onglet du navigateur, après http://localhost:3000/#.

Remarque

Vous devrez peut-être installer une dépendance supplémentaire pour rendre cette démonstration compatible avec Webpack 5. Si vous recevez une erreur de compilation liée à un package « buffer » ou « url », exécutez npm install -D buffer url et réessayez. Ce problème sera résolu dans une version ultérieure de l’infrastructure Fluid.

Étapes suivantes

  • Essayez d’étendre la démonstration avec d’autres paires clé/valeur dans le champ additionalDetails dans userConfig.
  • Envisagez d’intégrer l’audience dans une application collaborative qui utilise des structures de données distribuées, comme SharedMap ou SharedString.
  • En savoir plus sur Audience.

Conseil

Lorsque vous apportez des modifications au code, le projet est automatiquement régénéré et le serveur d’applications est rechargé. Toutefois, si vous apportez des modifications au schéma du conteneur, elles ne prendront effet que si vous fermez et redémarrez le serveur d’applications. Pour ce faire, rendez l’invite de commandes focale et appuyez deux fois sur Ctrl+C. Puis exécutez npm run start à nouveau.