Compartir a través de


Funcionalidades principales de Live Share

Captura de pantalla que muestra un ejemplo de los usuarios que juegan un juego de póquer ágil en una reunión de Teams, que muestra la funcionalidad de recursos compartidos en vivo.

El SDK de Live Share se puede agregar a los contextos de sidePanel y meetingStage de la extensión de reunión con un esfuerzo mínimo. También puede usar el SDK en contextos de chat y canal content , como pestañas configurables, pestañas estáticas y vista previa de colaboración.

Nota:

Los contextos de Live Share content en chats y canales solo se admiten en los clientes de escritorio y web de Teams.

Este artículo se centra en cómo integrar el SDK de Live Share en la aplicación y las funcionalidades clave del SDK.

Requisitos previos

Instalación del SDK de JavaScript

El SDK de Live Share es un paquete de JavaScript publicado en npm y se puede descargar a través de npm o yarn. También debe instalar las dependencias del mismo nivel de Live Share, que incluyen fluid-framework y @fluidframework/azure-client. Si usa Live Share en la aplicación de pestaña, también debe instalar @microsoft/teams-js la versión 2.23.0 o posterior. Si desea usar la clase para el TestLiveShareHost desarrollo del explorador local, debe instalar @fluidframework/test-client-utils y start-server-and-test paquetes en devDependencies.

NPM

npm install @microsoft/live-share fluid-framework @fluidframework/azure-client --save
npm install @microsoft/teams-js --save
npm install @fluidframework/test-client-utils start-server-and-test --save-dev

Yarn

yarn add @microsoft/live-share fluid-framework @fluidframework/azure-client
yarn add @microsoft/teams-js
yarn add @fluidframework/test-client-utils -dev

Registrar permisos de RSC

Para habilitar el SDK de Live Share para la extensión de pestaña, primero debe agregar los siguientes permisos de RSC al manifiesto de la aplicación:

{
  // ...rest of your manifest here
  "configurableTabs": [
    {
        "configurationUrl": "<<YOUR_CONFIGURATION_URL>>",
        "canUpdateConfiguration": true,
        "scopes": [
            "groupchat",
            "team"
        ],
        "context": [
            // meeting contexts
            "meetingSidePanel",
            "meetingStage",
            // content contexts
            "privateChatTab",
            "channelTab",
            "meetingChatTab"
        ]
    }
  ],
  "validDomains": [
    "<<BASE_URI_ORIGIN>>"
  ],
  "authorization": {​
    "permissions": {​
      "resourceSpecific": [
        // ...other permissions here​
        {​
          "name": "LiveShareSession.ReadWrite.Chat",​
          "type": "Delegated"
        },
        {​
          "name": "LiveShareSession.ReadWrite.Group",​
          "type": "Delegated"
        },
        {​
          "name": "MeetingStage.Write.Chat",​
          "type": "Delegated"
        },
        {​
          "name": "ChannelMeetingStage.Write.Group",​
          "type": "Delegated"
        }
      ]​
    }​
  }​
}

Unirse a una sesión

Siga los pasos para unirse a una sesión asociada a la reunión, el chat o el canal de un usuario:

  1. Inicialice LiveShareClient.
  2. Defina las estructuras de datos que desea sincronizar. Por ejemplo, LiveState o SharedMap.
  3. Únase al contenedor.

Ejemplo:

import { LiveShareClient, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    liveState: LiveState,
    sharedMap: SharedMap,
  },
};
const { container } = await liveShare.joinContainer(schema);

// ... ready to start app sync logic

Eso es todo lo que se tardó en configurar el contenedor y unirse a la sesión asignada a la reunión, el chat o el canal. Ahora, vamos a revisar los distintos tipos de estructuras de datos distribuidos que puede usar con el SDK de Live Share.

Sugerencia

Asegúrese de que el SDK de cliente de Teams se inicializa antes de llamar a LiveShareHost.create().

Estructuras de datos de Live Share

El SDK de Live Share incluye un conjunto de nuevas estructuras de datos distribuidos que amplían la clase de DataObject Fluid, lo que proporciona nuevos tipos de objetos con estado y sin estado. A diferencia de las estructuras de datos de Fluid, las clases de LiveDataObject Live Share no escriben cambios en el contenedor de Fluid, lo que permite una sincronización más rápida. Además, estas clases se diseñaron desde cero para escenarios comunes en reuniones, chats y canales de Teams. Los escenarios comunes incluyen la sincronización del contenido que está viendo el moderador, la visualización de metadatos para cada usuario de la sesión o la visualización de un temporizador de cuenta atrás.

Objeto Live Descripción
LivePresence Vea qué usuarios están en línea, establezca propiedades personalizadas para cada usuario y difunda los cambios en su presencia.
LiveState Sincronice cualquier valor serializable state JSON.
LiveTimer Sincronice un temporizador de cuenta atrás para un intervalo determinado.
LiveEvent Difunda eventos individuales con atributos de datos personalizados en la carga.
LiveFollowMode Siga usuarios específicos, presente a todos los usuarios de la sesión e inicie o finalice las suspensiones.

Ejemplo de LivePresence

Captura de pantalla que muestra un ejemplo de personas que están disponibles en una sesiónTeams mediante la presencia de Live Share.

La LivePresence clase hace que el seguimiento de quién está en la sesión sea más fácil que nunca. Al llamar a los .initialize() métodos o .updatePresence() , puede asignar metadatos personalizados para ese usuario, como la imagen de perfil, el identificador del contenido que está viendo, etc. Al escuchar presenceChanged eventos, cada cliente recibe el objeto más reciente LivePresenceUser y contrae todas las actualizaciones de presencia en un único registro para cada único userId.

A continuación se muestran algunos ejemplos en los que LivePresence se puede usar en la aplicación:

  • Obtención de Microsoft Teams userId, displayNamey roles de cada usuario de la sesión.
  • Mostrar información personalizada sobre cada usuario conectado a la sesión, como una dirección URL de imagen de perfil.
  • Sincronizar las coordenadas en una escena 3D donde se encuentra el avatar de cada usuario.
  • Informe de la posición del cursor de cada usuario en un documento de texto.
  • Publicar la respuesta de cada usuario a una pregunta de separador de hielo durante una actividad de grupo.
import {
  LiveShareClient,
  LivePresence,
  PresenceState,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    presence: LivePresence,
  },
};
const { container } = await liveShare.joinContainer(schema);
const presence = container.initialObjects.presence;

// Register listener for changes to each user's presence.
// This should be done before calling `.initialize()`.
presence.on("presenceChanged", (user, local) => {
  console.log("A user presence changed:");
  console.log("- display name:", user.displayName);
  console.log("- state:", user.state);
  console.log("- custom data:", user.data);
  console.log("- change from local client", local);
  console.log("- change impacts local user", user.isLocalUser);
});

// Define the initial custom data for the local user (optional).
const customUserData = {
  picture: "DEFAULT_PROFILE_PICTURE_URL",
  readyToStart: false,
};
// Start receiving incoming presence updates from the session.
// This will also broadcast the user's `customUserData` to others in the session.
await presence.initialize(customUserData);

// Send a presence update, in this case once a user is ready to start an activity.
// If using role verification, this will throw an error if the user doesn't have the required role.
await presence.update({
  ...customUserData,
  readyToStart: true,
});

Los usuarios que se unen a una sesión desde un único dispositivo tienen un único LivePresenceUser registro que se comparte con todos sus dispositivos. Para acceder a la versión más reciente data y state a cada una de sus conexiones activas, puede usar la getConnections() API desde la LivePresenceUser clase . Esto devuelve una lista de LivePresenceConnection objetos. Puede ver si una instancia determinada LivePresenceConnection procede del dispositivo local mediante la isLocalConnection propiedad .

Cada LivePresenceUser instancia de y LivePresenceConnection tiene una state propiedad , que puede ser online, offlineo away. Cuando presenceChanged cambia el estado de un usuario, se genera un evento. Por ejemplo, si un usuario se desconecta de la sesión o cierra la aplicación, su estado cambia a offline.

Nota:

Los datos pueden tardar hasta 20 segundos LivePresenceUseren actualizarse offline después de state que un usuario se desconecte de la sesión.

Ejemplo de LiveState

Captura de pantalla que muestra un ejemplo del estado de Live Share para sincronizar qué planeta del sistema solar se presenta activamente a la reunión.

La LiveState clase permite sincronizar el estado de la aplicación simple para los participantes conectados. LiveState sincroniza un único state valor, lo que le permite sincronizar cualquier valor serializable json, como string, numbero object.

A continuación se muestran algunos ejemplos en los que LiveState se puede usar en la aplicación:

  • Establecer el identificador de usuario del moderador actual para compilar una característica de toma de control .
  • Sincronizar la ruta de acceso de ruta actual de la aplicación para asegurarse de que todos están en la misma página. Por ejemplo, /whiteboard/:whiteboardId.
  • Mantener el identificador de contenido que está viendo el moderador actual. Por ejemplo, en taskId un panel de tareas.
  • Sincronización del paso actual en una actividad de grupo de varias rondas. Por ejemplo, la fase de adivinación durante el juego agile poker.
  • Mantener una posición de desplazamiento sincronizada para una característica de sígueme .

Nota:

A diferencia SharedMapde , el state valor de LiveState se restablece después de que todos los usuarios se desconecten de una sesión.

Ejemplo:

import { LiveShareClient, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { appState: LiveState },
};
const { container } = await liveShare.joinContainer(schema);
const { appState } = container.initialObjects;

// Register listener for changes to the state.
// This should be done before calling `.initialize()`.
appState.on("stateChanged", (planetName, local, clientId) => {
  // Update app with newly selected planet.
  // See which user made the change (optional)
  const clientInfo = await appState.getClientInfo(clientId);
});

// Set a default value and start listening for changes.
// This default value will not override existing for others in the session.
const defaultState = "Mercury";
await appState.initialize(defaultState);

// `.set()` will change the state for everyone in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
await appState.set("Earth");

Ejemplo de LiveEvent

Captura de pantalla que muestra un ejemplo de cliente de Teams que muestra una notificación cuando hay un cambio en el evento.

LiveEvent es una excelente manera de enviar eventos simples a otros clientes conectados que solo son necesarios en el momento de la entrega. Resulta útil para escenarios como el envío de notificaciones de sesión o la implementación de reacciones personalizadas.

import { LiveEvent, LiveShareClient } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { customReactionEvent: LiveEvent },
};
const { container } = await liveShare.joinContainer(schema);
const { customReactionEvent } = container.initialObjects;

// Register listener to receive events sent through this object.
// This should be done before calling `.initialize()`.
customReactionEvent.on("received", async (kudosReaction, local, clientId) => {
  console.log("Received reaction:", kudosReaction, "from clientId", clientId);
  // See which user made the change (optional)
  const clientInfo = await customReactionEvent.getClientInfo(clientId);
  // Display notification in your UI
});

// Start listening for incoming events
await customReactionEvent.initialize();

// `.send()` will send your event value to everyone in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
const kudosReaction = {
  emoji: "❤️",
  forUserId: "SOME_OTHER_USER_ID",
};
await customReactionEvent.send(kudosReaction);

Ejemplo de LiveTimer

Captura de pantalla que muestra un ejemplo de un temporizador de recuento inactivo con 9 segundos restantes.

LiveTimer proporciona un temporizador de cuenta atrás simple que se sincroniza para todos los participantes conectados. Es útil para escenarios que tienen un límite de tiempo, como un temporizador de meditación en grupo o un temporizador de ronda para un juego. También puede usarlo para programar tareas para todos los usuarios de la sesión, como mostrar un aviso.

import { LiveShareClient, LiveTimer } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { timer: LiveTimer },
};
const { container } = await liveShare.joinContainer(schema);
const { timer } = container.initialObjects;

// Register listeners for timer changes
// This should be done before calling `.initialize()`.

// Register listener for when the timer starts its countdown
timer.on("started", (config, local) => {
  // Update UI to show timer has started
});

// Register listener for when a paused timer has resumed
timer.on("played", (config, local) => {
  // Update UI to show timer has resumed
});

// Register listener for when a playing timer has paused
timer.on("paused", (config, local) => {
  // Update UI to show timer has paused
});

// Register listener for when a playing timer has finished
timer.on("finished", (config) => {
  // Update UI to show timer is finished
});

// Register listener for the timer progressed by 20 milliseconds
timer.on("onTick", (milliRemaining) => {
  // Update UI to show remaining time
});

// Start synchronizing timer events for users in session
await timer.initialize();

// Start a 60 second timer for users in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
const durationInMilliseconds = 1000 * 60;
await timer.start(durationInMilliseconds);

// Pause the timer for users in session
// If using role verification, this will throw an error if the user doesn't have the required role.
await timer.pause();

// Resume the timer for users in session
// If using role verification, this will throw an error if the user doesn't have the required role.
await timer.play();

Ejemplo de LiveFollowMode

La imagen muestra tres clientes con tres vistas independientes: un moderador, un usuario que sigue al moderador y un usuario con su propia vista privada con la opción de sincronizar de nuevo con el moderador.

La LiveFollowMode clase combina LivePresence y LiveState en una sola clase, lo que le permite implementar fácilmente modos de seguidor y moderador en la aplicación. Esto le permite implementar patrones conocidos de aplicaciones colaborativas populares como PowerPoint Live, Excel Live y Whiteboard. A diferencia del uso compartido de pantalla, LiveFollowMode permite representar contenido con alta calidad, accesibilidad mejorada y rendimiento mejorado. Los usuarios pueden cambiar fácilmente entre sus vistas privadas y seguir a otros usuarios.

Puede usar la startPresenting() función para tomar el control de la aplicación para todos los demás usuarios de la sesión. Como alternativa, puede permitir que los usuarios seleccionen individualmente usuarios específicos que quieran seguir mediante la followUser() función . En ambos escenarios, los usuarios pueden escribir temporalmente una vista privada con la beginSuspension() función o sincronizar de nuevo con el moderador con la endSuspension() función. Mientras tanto, la update() función permite al usuario local informar a otros clientes de la sesión de su propio personal stateValue. De forma similar a LivePresence, puede escuchar los cambios de cada usuario a través de un presenceChanged agente de escucha de stateValue eventos.

LiveFollowMode también expone un state objeto, que se actualiza dinámicamente en función del usuario que sigue el usuario local. Por ejemplo, si el usuario local no sigue a nadie, la state.value propiedad coincide con la difusión más reciente stateValue del usuario local a través de update(). Sin embargo, si el usuario local sigue a un moderador, la state.value propiedad coincide con la más reciente stateValuedel usuario que la presenta. De forma similar a LiveState, puede escuchar los cambios en el state valor mediante un stateChanged agente de escucha de eventos. Para obtener más información sobre el objeto , vea Referencia de interfazstate IFollowModeState.

A continuación se muestran algunos ejemplos en los que puede usar LiveFollowMode en la aplicación:

  • Sincronice las posiciones de la cámara en una escena 3D con cobrowse durante una revisión de diseño.
  • Actualice para slideId que se abra en un carrusel para presentaciones y discusiones productivas.
  • Difunda para que se abra en el path enrutador de la aplicación.

Ejemplo:

import {
  LiveShareClient,
  LiveFollowMode,
  FollowModeType,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    followMode: LiveFollowMode,
  },
};
const { container } = await liveShare.joinContainer(schema);
const followMode = container.initialObjects.followMode;

// As an example, we will assume there is a button in the application document
const button = document.getElementById("action-button");
// As an example, we will assume there is a div with text showing the follow state
const infoText = document.getElementById("info-text");

// Register listener for changes to the `state` value to use in your app.
// This should be done before calling `.initialize()`.
followMode.on("stateChanged", (state, local, clientId) => {
  console.log("The state changed:");
  console.log("- state value:", state.value);
  console.log("- follow mode type:", state.type);
  console.log("- following user id:", state.followingUserId);
  console.log(
    "- count of other users also following user",
    state.otherUsersCount
  );
  console.log(
    "- state.value references local user's stateValue",
    state.isLocalValue
  );
  // Can optionally get the relevant user's presence object
  const followingUser = followMode.getUserForClient(clientId);
  switch (state.type) {
    case FollowModeType.local: {
      // Update app to reflect that the user isn't following anyone and there is no presenter.
      infoText.innerHTML = "";
      // Show a "Start presenting" button in your app.
      button.innerHTML = "Start presenting";
      button.onclick = followMode.startPresenting;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.activeFollowers: {
      // Update app to reflect that the local user is being followed by other users.
      infoText.innerHTML = `${state.otherUsersCount} users are following you`;
      // Does not mean that the local user is presenting to everyone, so you can still show the "Start presenting" button.
      button.innerHTML = "Present to all";
      button.onclick = followMode.startPresenting;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.activePresenter: {
      // Update app to reflect that the local user is actively presenting to everyone.
      infoText.innerHTML = `You are actively presenting to everyone`;
      // Show a "Stop presenting" button in your app.
      button.innerHTML = "Stop presenting";
      button.onclick = followMode.stopPresenting;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.followPresenter: {
      // The local user is following a remote presenter.
      infoText.innerHTML = `${followingUser?.displayName} is presenting to everyone`;
      // Show a "Take control" button in your app.
      button.innerHTML = "Take control";
      button.onclick = followMode.startPresenting;
      // Note: state.isLocalValue will be false.
      break;
    }
    case FollowModeType.suspendFollowPresenter: {
      // The local user is following a remote presenter but has an active suspension.
      infoText.innerHTML = `${followingUser?.displayName} is presenting to everyone`;
      // Show a "Sync to presenter" button in your app.
      button.innerHTML = "Sync to presenter";
      button.onclick = followMode.endSuspension;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.followUser: {
      // The local user is following a specific remote user.
      infoText.innerHTML = `You are following ${followingUser?.displayName}`;
      // Show a "Stop following" button in your app.
      button.innerHTML = "Stop following";
      button.onclick = followMode.stopFollowing;
      // Note: state.isLocalValue will be false.
      break;
    }
    case FollowModeType.suspendFollowUser: {
      // The local user is following a specific remote user but has an active suspension.
      infoText.innerHTML = `You were following ${followingUser?.displayName}`;
      // Show a "Resume following" button in your app.
      button.innerHTML = "Resume following";
      button.onclick = followMode.endSuspension;
      // Note: state.isLocalValue will be true.
      break;
    }
    default: {
      break;
    }
  }
  const newCameraPosition = state.value;
  // TODO: apply new camera position
});

// Register listener for changes to each user's personal state updates.
// This should be done before calling `.initialize()`.
followMode.on("presenceChanged", (user, local) => {
  console.log("A user presence changed:");
  console.log("- display name:", user.displayName);
  console.log("- state value:", user.data?.stateValue);
  console.log("- user id user is following:", user.data?.followingUserId);
  console.log("- change from local client", local);
  console.log("- change impacts local user", user.isLocalUser);
  // As an example, we will assume there is a button for each user in the session.
  document.getElementById(`follow-user-${user.userId}-button`).onclick = () => {
    followMode.followUser(user.userId);
  };
  // Update 3D scene to reflect this user's camera position (e.g., orb + display name)
  const userCameraPosition = user.data?.stateValue;
});

// Define the initial stateValue for the local user (optional).
const startingCameraPosition = {
  x: 0,
  y: 0,
  z: 0,
};
// Start receiving incoming presence updates from the session.
// This will also broadcast the user's `startingCameraPosition` to others in the session.
await followMode.initialize(startingCameraPosition);

// Example of an event listener for a camera position changed event.
// For something like a camera change event, you should use a debounce function to prevent sending updates too frequently.
// Note: it helps to distinguish changes initiated by the local user (e.g., drag mouse) separately from other change events.
function onCameraPositionChanged(position, isUserAction) {
  // Broadcast change to other users so that they have their latest camera position
  followMode.update(position);
  // If the local user changed the position while following another user, we want to suspend.
  // Note: helps to distinguish changes initiated by the local user (e.g., drag mouse) separately from other change events.
  if (!isUserAction) return;
  switch (state.type) {
    case FollowModeType.followPresenter:
    case FollowModeType.followUser: {
      // This will trigger a "stateChanged" event update for the local user only.
      followMode.beginSuspension();
      break;
    }
    default: {
      // No need to suspend for other types
      break;
    }
  }
}

En meetingStage contextos, los usuarios colaboran y presentan sincrónicamente para facilitar discusiones más productivas. Cuando un usuario presenta contenido a la fase de reunión, debe llamar a la startPresenting() API para el moderador inicial. En content contextos como la vista previa de colaboración, el contenido se consume de forma asincrónica con mayor frecuencia. En este caso, es mejor permitir que los usuarios opten por la colaboración en tiempo real, como mediante un botón "Seguir". Con la teamsJs.app.getContext() API del SDK de JavaScript de Teams, puede ajustar fácilmente la funcionalidad en consecuencia.

Ejemplo:

import {
  LiveShareClient,
  LiveFollowMode,
  FollowModeType,
} from "@microsoft/live-share";
import {
  app,
  meeting,
  FrameContexts,
  LiveShareHost,
} from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    followMode: LiveFollowMode,
  },
};
const { container } = await liveShare.joinContainer(schema);
const followMode = container.initialObjects.followMode;

// Get teamsJs context
const context = await app.getContext();
// Take control if in meetingStage context and local user is initial presenter
if (context.page?.frameContext === FrameContexts.meetingStage) {
  // Take control if in meetingStage context and local user is initial presenter
  meeting.getAppContentStageSharingState((error, state) => {
    const isShareInitiator = state?.isShareInitiator;
    if (!isShareInitiator) return;
    // The user is the initial presenter, so we "take control"
    await followMode.startPresenting();
  });
}
// TODO: rest of app logic

Comprobación de roles para estructuras de datos activas

Las reuniones en Teams incluyen llamadas, reuniones de manos y clases en línea. Los participantes de la reunión pueden abarcar organizaciones, tener privilegios diferentes o tener objetivos diferentes. Por lo tanto, es importante respetar los privilegios de los distintos roles de usuario durante las reuniones. Los objetos dinámicos están diseñados para admitir la comprobación de roles, lo que le permite definir los roles que pueden enviar mensajes para cada objeto dinámico individual. Por ejemplo, ha seleccionado la opción que permite que solo los moderadores y organizadores de reuniones controlen la reproducción de vídeo. Sin embargo, los invitados y asistentes todavía pueden solicitar los siguientes vídeos para verlos.

Nota:

Al acceder a Live Share desde un content contexto de chat o canal, todos los usuarios tendrán los Organizer roles y Presenter .

En el ejemplo siguiente, donde solo los moderadores y organizadores pueden tomar el control, LiveState se usa para sincronizar qué usuario es el moderador activo:

import {
  LiveShareClient,
  LiveState,
  UserMeetingRole,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { appState: LiveState },
};
const { container } = await liveShare.joinContainer(schema);
const { appState } = container.initialObjects;

// Register listener for changes to state
appState.on("stateChanged", (state, local) => {
  // Update local app state
});

// Set roles who can change state and start listening for changes
const initialState = {
  documentId: "INITIAL_DOCUMENT_ID",
};
const allowedRoles = [UserMeetingRole.organizer, UserMeetingRole.presenter];
await appState.initialize(initialState, allowedRoles);

async function onSelectEditMode(documentId) {
  try {
    await appState.set({
      documentId,
    });
  } catch (error) {
    console.error(error);
  }
}

async function onSelectPresentMode(documentId) {
  try {
    await appState.set({
      documentId,
      presentingUserId: "LOCAL_USER_ID",
    });
  } catch (error) {
    console.error(error);
  }
}

Escuche a los clientes para comprender sus escenarios antes de implementar la comprobación de roles en la aplicación, especialmente para el rol Organizador. No hay ninguna garantía de que un organizador de la reunión esté presente en la reunión. Como regla general, todos los usuarios son Organizador o Moderador al colaborar dentro de una organización. Si un usuario es un Asistente, suele ser una decisión intencionada en nombre de un organizador de la reunión.

En algunos casos, un usuario podría tener varios roles. Por ejemplo, un organizador también es un moderador. Además, los participantes de la reunión que son externos al inquilino que hospeda la reunión tienen el rol Invitado , pero también pueden tener privilegios de moderador . Esto proporciona más flexibilidad en el uso de la verificación de roles en la aplicación.

Nota:

El SDK de Live Share no es compatible con los usuarios invitados en las reuniones de canal.

Estructuras de datos distribuidos fluidos

El SDK de Live Share admite cualquier estructura de datos distribuida incluida en Fluid Framework. Estas características sirven como un conjunto de primitivos que puede usar para crear escenarios de colaboración sólidos, como actualizaciones en tiempo real de una lista de tareas o coautoría de texto dentro de un html <textarea>.

A diferencia de las LiveDataObject clases mencionadas en este artículo, las estructuras de datos fluid no se restablecen después de cerrar la aplicación. Esto es ideal para escenarios como la reunión sidePanel y content los contextos, donde los usuarios cierran y vuelven a abrir la aplicación con frecuencia.

Fluid Framework admite oficialmente los siguientes tipos de estructuras de datos distribuidas:

Objeto compartido Descripción
SharedMap Almacén de clave-valor distribuido. Establezca cualquier objeto serializable con JSON para que una clave determinada sincronice ese objeto para todos los usuarios de la sesión.
SharedSegmentSequence Estructura de datos de tipo lista para almacenar un conjunto de elementos (denominados segmentos) en posiciones de conjunto.
SharedString Secuencia de cadena distribuida optimizada para editar el texto de documentos o áreas de texto.

Veamos cómo funciona SharedMap. En este ejemplo, hemos usado SharedMap para crear una característica de lista de reproducción.

import { LiveShareClient } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { playlistMap: SharedMap },
};
const { container } = await liveShare.joinContainer(schema);
const playlistMap = container.initialObjects.playlistMap;

// Register listener for changes to values in the map
playlistMap.on("valueChanged", (changed, local) => {
  const video = playlistMap.get(changed.key);
  // Update UI with added video
});

function onClickAddToPlaylist(video) {
  // Add video to map
  playlistMap.set(video.id, video);
}

Nota:

Los objetos DDS de Core Fluid Framework no admiten la comprobación de roles de reunión. Todos los usuarios de la reunión pueden cambiar los datos almacenados a través de estos objetos.

Pruebas del explorador local

Puede probar localmente el SDK de Live Share en el explorador mediante la clase sin instalar la TestLiveShareHost aplicación en Teams. Esto es útil para probar las capacidades de colaboración básicas de la aplicación dentro de un entorno familiar localhost .

Ejemplo:

import {
  LiveShareClient,
  TestLiveShareHost,
  LiveState,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

/**
 * Detect whether you are in Teams or local environment using your preferred method.
 * Options for this include: environment variables, URL params, Teams FX, etc.
 */
const inTeams = process.env.IN_TEAMS;
// Join the Fluid container
const host = inTeams ? LiveShareHost.create() : TestLiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    liveState: LiveState,
    sharedMap: SharedMap,
  },
};
const { container } = await liveShare.joinContainer(schema);

// ... ready to start app sync logic

La TestLiveShareHost clase usa el tinylicious servidor de prueba de Fluid Framework, en lugar de nuestro servicio de producción de Azure Fluid Relay. Para ello, debe agregar algunos scripts a package.json para iniciar el servidor de prueba. También debe agregar los @fluidframework/test-client-utils paquetes y start-server-and-test a en devDependenciespackage.json.

{
  "scripts": {
    "start": "start-server-and-test start:server 7070 start:client",
    "start:client": "{YOUR START CLIENT COMMAND HERE}",
    "start:server": "npx tinylicious@latest"
  },
  "devDependencies": {
    "@fluidframework/test-client-utils": "^1.3.6",
    "start-server-and-test": "^2.0.0"
  }
}

Al iniciar la aplicación de esta manera, se agrega #{containerId} a la LiveShareClient dirección URL, si no existe. A continuación, puede copiar y pegar la dirección URL en una nueva ventana del explorador para conectarse al mismo contenedor Fluid.

Nota:

De forma predeterminada, todos los clientes conectados a través TestLiveShareHost de tendrán presenter roles y organizer .

Ejemplos de código

Ejemplo de nombre Descripción JavaScript TypeScript
Dice Roller Habilite a todos los clientes conectados para que lancen el dado y vean el resultado. View Ver
Agile Poker Permitir que todos los clientes conectados jueguen a Agile Poker. View ND
Modelo 3D Permitir que todos los clientes conectados vean un modelo 3D juntos. ND View
Temporizador Habilite todos los clientes conectados para ver un temporizador de cuenta atrás. ND Ver
Avatares de presencia Mostrar avatares de presencia para todos los clientes conectados. ND View

Paso siguiente

Vea también