Condividi tramite


Procedura: Usare le funzionalità del gruppo di destinatari in Fluid Framework

In questa esercitazione si apprenderà come usare Fluid Framework Audience con React per creare una dimostrazione visiva degli utenti che si connettono a un contenitore. L'oggetto gruppo di destinatari contiene informazioni correlate a tutti gli utenti connessi al contenitore. In questo esempio verrà usata la libreria client di Azure per creare il contenitore e il gruppo di destinatari.

L'immagine seguente mostra i pulsanti ID e un campo di input DELL'ID contenitore. Lasciando vuoto il campo ID contenitore e facendo clic su un pulsante ID utente verrà creato un nuovo contenitore e aggiunto come utente selezionato. In alternativa, l'utente finale può immettere un ID contenitore e scegliere un ID utente per aggiungere un contenitore esistente come utente selezionato.

Screenshot di un browser con pulsanti per la selezione di un utente.

L'immagine successiva mostra più utenti connessi a un contenitore rappresentato da caselle. La casella evidenziata in blu rappresenta l'utente che visualizza il client mentre le caselle evidenziate in nero rappresentano gli altri utenti connessi. Man mano che i nuovi utenti si collegano al contenitore con ID univoco, il numero di caselle aumenterà.

Screenshot di un browser che mostra le informazioni per quattro utenti del contenitore diversi.

Nota

Questa esercitazione presuppone che si abbia familiarità con La panoramica di Fluid Framework e che sia stata completata la guida introduttiva. È anche necessario avere familiarità con le nozioni di base di React, la creazione di progetti React e gli hook React.

Creare il progetto

  1. Aprire un prompt dei comandi e passare alla cartella padre in cui si vuole creare il progetto; ad esempio . C:\My Fluid Projects

  2. Eseguire il comando seguente al prompt. Si noti che l'interfaccia della riga di comando è npx, non npm. È stato installato quando è stato installato Node.js.

    npx create-react-app fluid-audience-tutorial
    
  3. Il progetto viene creato in una sottocartella denominata fluid-audience-tutorial. Passare a esso con il comando cd fluid-audience-tutorial.

  4. Il progetto usa le librerie Fluid seguenti:

    Libreria Descrizione
    fluid-framework Contiene la struttura di dati distribuiti SharedMap che sincronizza i dati tra i client.
    @fluidframework/azure-client Definisce la connessione a un server di servizio Fluid e definisce lo schema iniziale per il contenitore Fluid.
    @fluidframework/test-client-utils Definisce InsecureTokenProvider necessario per creare la connessione a un servizio fluido.

    Eseguire il comando seguente per installare le librerie.

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

Scrivere il codice del progetto

Configurare le variabili di stato e la visualizzazione dei componenti

  1. Aprire il file \src\App.js nell'editor di codice. Eliminare tutte le istruzioni predefinite import . Eliminare quindi tutto il markup dall'istruzione return . Aggiungere quindi istruzioni di importazione per i componenti e gli hook React. Si noti che i componenti AudienceDisplay e UserIdSelection importati verranno eseguiti nei passaggi successivi. Il file dovrebbe avere un aspetto simile al seguente:

        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. Sostituisci TODO 1 con il codice seguente. Questo codice inizializza le variabili di stato locale che verranno usate all'interno dell'applicazione. Il displayAudience valore determina se viene eseguito il rendering del componente AudienceDisplay o del componente UserIdSelection (vedere TODO 2). Il userId valore è l'identificatore utente con cui connettersi al contenitore e il containerId valore è il contenitore da caricare. Le handleSelectUser funzioni e handleContainerNotFound vengono passate come callback alle due visualizzazioni e gestiscono le transizioni di stato. handleSelectUser viene chiamato quando si tenta di creare/caricare un contenitore. handleContainerNotFound viene chiamato quando la creazione/caricamento di un contenitore ha esito negativo.

    Si noti che i valori userId e containerId provengono da un componente UserIdSelection tramite la handleSelectUser funzione .

        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. Sostituisci TODO 2 con il codice seguente. Come indicato in precedenza, la displayAudience variabile determinerà se viene eseguito il rendering del componente AudienceDisplay o del componente UserIdSelection . Inoltre, le funzioni per aggiornare le variabili di stato vengono passate ai componenti come proprietà.

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

Configurare il componente AudienceDisplay

  1. Creare e aprire un file \src\AudienceDisplay.js nell'editor di codice. Aggiungere le istruzioni import seguenti:

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

    Si noti che gli oggetti importati dalla libreria Fluid Framework sono necessari per definire utenti e contenitori. Nei passaggi seguenti, AzureClient e InsecureTokenProvider verranno usati per configurare il servizio client (vedere TODO 1) mentre SharedMap verrà usato per configurare una containerSchema necessità per creare un contenitore (vedere TODO 2).

  2. Aggiungere i componenti funzionali e le funzioni helper seguenti:

        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
        }
    

    Si noti che AudienceDisplay e AudienceList sono componenti funzionali che gestiscono il recupero e il rendering dei dati dei destinatari mentre il tryGetAudienceObject metodo gestisce la creazione di servizi contenitore e destinatari.

Ottenere contenitori e destinatari

È possibile usare una funzione helper per ottenere i dati Fluid, dall'oggetto Audience, al livello di visualizzazione (stato React). Il tryGetAudienceObject metodo viene chiamato quando il componente di visualizzazione viene caricato dopo che è stato selezionato un ID utente. Il valore restituito viene assegnato a una proprietà di stato React.

  1. Sostituisci TODO 1 con il codice seguente. Si noti che i valori per userId userName containerId verranno passati dal componente App. Se non containerIdè presente , viene creato un nuovo contenitore. Si noti anche che l'oggetto containerId viene archiviato nell'hash url. Un utente che immette una sessione da un nuovo browser può copiare l'URL da un browser di sessione esistente oppure passare a localhost:3000 e immettere manualmente l'ID contenitore. Con questa implementazione, si vuole eseguire il wrapping della getContainer chiamata in un tentativo catch nel caso in cui l'utente inserisca un ID contenitore che non esiste. Per altre informazioni, vedere la documentazione relativa ai contenitori .

        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;
    

Ottenere il gruppo di destinatari sul montaggio dei componenti

Ora che è stato definito come ottenere il pubblico Fluido, è necessario indicare a React di chiamare tryGetAudienceObject quando viene montato il componente Audience Display.

  1. Sostituisci TODO 2 con il codice seguente. Si noti che l'ID utente proviene dal componente padre come user1 user2 o random. Se l'ID viene random usato Math.random() per generare un numero casuale come ID. Inoltre, verrà eseguito il mapping di un nome all'utente in base al relativo ID, come specificato in userNameList. Infine, definiamo le variabili di stato che archivieranno i membri connessi e l'utente corrente. fluidMembers archivierà un elenco di tutti i membri connessi al contenitore, mentre currentMember conterrà l'oggetto membro che rappresenta l'utente corrente che visualizza il contesto del browser.

        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. Sostituisci TODO 3 con il codice seguente. Verrà chiamato quando tryGetAudienceObject il componente viene montato e impostare i membri del gruppo di destinatari restituiti su fluidMembers e currentMember. Si noti che viene verificato se viene restituito un oggetto audience nel caso in cui un utente inserisca un containerId che non esiste ed è necessario restituirli alla visualizzazione UserIdSelection (props.onContainerNotFound() gestirà il cambio della visualizzazione). Inoltre, è consigliabile annullare la registrazione dei gestori eventi quando il componente React viene smontato restituendo 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. Sostituisci TODO 4 con il codice seguente. Se o fluidMembers currentMember non è stato inizializzato, viene eseguito il rendering di una schermata vuota. Il componente AudienceList eseguirà il rendering dei dati del membro con stili (da implementare nella sezione successiva).

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

    Nota

    Le transizioni di connessione possono comportare brevi finestre di intervallo in cui getMyself restituisce undefined. Ciò è dovuto al fatto che la connessione client corrente non è stata ancora aggiunta al gruppo di destinatari, quindi non è possibile trovare un ID di connessione corrispondente. Per impedire a React di eseguire il rendering di una pagina senza membri del gruppo di destinatari, aggiungere un listener per chiamare updateMembers su membersChanged. Ciò funziona perché il gruppo di destinatari del servizio genera un membersChanged evento quando il contenitore è connesso.

Creare la visualizzazione

  1. Sostituisci TODO 5 con il codice seguente. Si noti che viene eseguito il rendering di un componente elenco per ogni membro passato dal componente AudienceDisplay . Per ogni membro, viene prima confrontato member.userId con currentMember.userId per verificare se tale membro isSelf. In questo modo, è possibile distinguere l'utente client dagli altri utenti e visualizzare il componente con un colore diverso. Si esegue quindi il push del componente elenco in una list matrice. Ogni componente visualizzerà i dati dei membri, userId userName ad esempio e 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. Sostituisci TODO 6 con il codice seguente. Verrà eseguito il rendering di tutti gli elementi membro di cui è stato eseguito il push nella list matrice.

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

Configurare il componente UserIdSelection

  1. Creare e aprire un file \src\UserIdSelection.js nell'editor di codice. Questo componente includerà i pulsanti ID utente e i campi di input dell'ID contenitore che consentono agli utenti finali di scegliere l'ID utente e la sessione collaborativa. Aggiungere le istruzioni e i componenti funzionali seguenti import :

    import { useState } from 'react';
    
    export const UserIdSelection = (props) => {
        // TODO 1: Define styles and handle user inputs
        return (
        // TODO 2: Return view components
        );
    }
    
  2. Sostituisci TODO 1 con il codice seguente. Si noti che la onSelectUser funzione aggiornerà le variabili di stato nel componente app padre e richiederà una modifica della visualizzazione. Il handleSubmit metodo viene attivato dagli elementi pulsante che verranno implementati in TODO 2. Inoltre, il handleChange metodo viene usato per aggiornare la containerId variabile di stato. Questo metodo verrà chiamato da un listener di eventi dell'elemento di input implementato in TODO 2. Si noti anche che si aggiorna il containerId valore ottenuto da un elemento HTML con l'ID containerIdInput (definito in 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. Sostituisci TODO 2 con il codice seguente. Verrà eseguito il rendering dei pulsanti ID utente e del campo di input dell'ID contenitore.

        <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>
    

Avviare il server Fluid ed eseguire l'applicazione

Nota

Per trovare la corrispondenza con il resto di questa procedura, questa sezione usa npx i comandi e npm per avviare un server Fluid. Tuttavia, il codice di questo articolo può essere eseguito anche su un server di inoltro fluido di Azure. Per altre informazioni, vedere Procedura: Effettuare il provisioning di un servizio Inoltro fluido di Azure e Procedura: Connettersi a un servizio di inoltro fluido di Azure

Nel prompt dei comandi eseguire il comando seguente per avviare il servizio Fluid.

npx @fluidframework/azure-local-service@latest

Aprire un nuovo prompt dei comandi e passare alla radice del progetto; ad esempio . C:/My Fluid Projects/fluid-audience-tutorial Avviare il server applicazioni con il comando seguente. L'applicazione viene aperta nel browser. Il processo può richiedere alcuni minuti.

npm run start

Passare a localhost:3000 in una scheda del browser per visualizzare l'applicazione in esecuzione. Per creare un nuovo contenitore, selezionare un pulsante ID utente lasciando vuoto l'input dell'ID contenitore. Per simulare un nuovo utente che partecipa alla sessione del contenitore, aprire una nuova scheda del browser e passare a localhost:3000. Questa volta, immettere il valore dell'ID contenitore che può essere trovato dall'URL della prima scheda del browser che procede http://localhost:3000/#.

Nota

Potrebbe essere necessario installare una dipendenza aggiuntiva per rendere questa demo compatibile con Webpack 5. Se viene visualizzato un errore di compilazione correlato a un pacchetto "buffer" o "url", eseguire npm install -D buffer url e riprovare. Questo problema verrà risolto in una versione futura di Fluid Framework.

Passaggi successivi

  • Provare a estendere la demo con più coppie chiave/valore nel additionalDetails campo in userConfig.
  • Prendere in considerazione l'integrazione di gruppi di destinatari in un'applicazione collaborativa che usa strutture di dati distribuite, ad esempio SharedMap o SharedString.
  • Altre informazioni sul gruppo di destinatari.

Suggerimento

Quando si apportano modifiche al codice, il progetto verrà ricompilato automaticamente e il server applicazioni verrà ricaricato. Tuttavia, se si apportano modifiche allo schema del contenitore, verranno applicate solo se si chiude e si riavvia il server applicazioni. A tale scopo, assegnare lo stato attivo al prompt dei comandi e premere CTRL+C due volte. Quindi eseguire npm run start di nuovo.