Principais recursos do Live Share

A captura de tela mostra um exemplo de usuários jogando um jogo de pôquer ágil em uma reunião do Teams, que mostra a funcionalidade de compartilhamento ao vivo.

O SDK do Live Share pode ser adicionado aos contextos sidePanel e meetingStage da extensão da sua reunião com o mínimo de esforço.

Este artigo se concentra em como integrar o SDK do Live Share ao seu aplicativo e aos principais recursos do SDK.

Pré-requisitos

Instalar o SDK do JavaScript

O SDK do Live Share é um pacote JavaScript publicado no npm e você pode baixar por meio de npm ou yarn. Você também deve instalar dependências de par do Live Share, que incluem fluid-framework e @fluidframework/azure-client. Se você estiver usando o Live Share em seu aplicativo de guia, também deve instalar @microsoft/teams-js a versão 2.11.0 ou posterior.

npm

npm install @microsoft/live-share fluid-framework @fluidframework/azure-client --save
npm install @microsoft/teams-js --save

Fio

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

Registrar permissões de RSC

Para habilitar o SDK do Live Share para sua extensão de reunião, adicione primeiramente as seguintes permissões de RSC ao manifesto do seu aplicativo:

{
  // ...rest of your manifest here
  "configurableTabs": [
    {
        "configurationUrl": "<<YOUR_CONFIGURATION_URL>>",
        "canUpdateConfiguration": true,
        "scopes": [
            "groupchat"
        ],
        "context": [
            "meetingSidePanel",
            "meetingStage"
        ]
    }
  ],
  "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"
        }
      ]​
    }​
  }​
}

Ingressar em uma sessão de reunião

Siga as etapas para ingressar em uma sessão associada à reunião de um usuário:

  1. Inicializar LiveShareClient.
  2. Defina as estruturas de dados que você deseja sincronizar. Por exemplo: LiveState ou SharedMap.
  3. Ingressar no contêiner.

Exemplo:

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

Isso é tudo o que foi necessário para configurar seu contêiner e ingressar na sessão da reunião. Agora, vamos revisar os diferentes tipos de estruturas de dados distribuídos que você pode usar com o SDK do Live Share.

Dica

Verifique se o SDK do Cliente do Teams é inicializado antes de chamar LiveShareHost.create().

Estruturas de dados do Live Share

O SDK do Live Share inclui um conjunto de novas estruturas de dados distribuídos que estendem a classe do DataObject Fluid, fornecendo novos tipos de objetos com estado e sem estado. Ao contrário das estruturas de dados fluidas, as classes do LiveDataObject Live Share não gravam alterações no contêiner Fluid, permitindo uma sincronização mais rápida. Além disso, essas classes foram projetadas do zero para cenários comuns de reunião em reuniões do Teams. Cenários comuns incluem sincronizar o conteúdo que o apresentador está exibindo, exibir metadados para cada usuário na reunião ou exibir um temporizador de contagem regressiva.

Objeto Live Descrição
LivePresence Veja quais usuários estão online, defina propriedades personalizadas para cada usuário e transmita as alterações em sua presença.
Livestate Sincronizar qualquer valor serializável state JSON.
LiveTimer Sincronizar um temporizador de contagem regressiva para um determinado intervalo.
LiveEvent Transmita eventos individuais com quaisquer atributos de dados personalizados no conteúdo.

Exemplo livePresence

A captura de tela mostra um exemplo de mostrar pessoas que estão disponíveis em um sessionTeams usando a presença do Live Share.

A LivePresence classe torna o acompanhamento de quem está na sessão mais fácil do que nunca. Ao chamar os .initialize() métodos ou .updatePresence() , você pode atribuir metadados personalizados para esse usuário, como imagem de perfil, identificador de conteúdo que eles estão exibindo e muito mais. Ao ouvir presenceChanged eventos, cada cliente recebe o objeto mais recente LivePresenceUser , colapsando todas as atualizações de presença em um único registro para cada único userId.

Veja a seguir alguns exemplos em que LivePresence podem ser usados em seu aplicativo:

  • Obtendo o Microsoft Teams userId, displayNamee roles de cada usuário na sessão.
  • Exibindo informações personalizadas sobre cada usuário conectado à sessão, como uma URL de imagem de perfil.
  • Sincronizando as coordenadas em uma cena 3D em que o avatar de cada usuário está localizado.
  • Relatar a posição do cursor de cada usuário em um documento de texto.
  • Postando a resposta de cada usuário a uma pergunta de quebra de gelo durante uma atividade 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,
});

Os usuários que ingressarem em uma sessão de um único dispositivo têm um único LivePresenceUser registro compartilhado para todos os seus dispositivos. Para acessar o mais recente data e state para cada uma de suas conexões ativas, você pode usar a getConnections() API da LivePresenceUser classe. Isso retorna uma lista de LivePresenceConnection objetos. Você pode ver se uma determinada LivePresenceConnection instância é do dispositivo local usando a isLocalConnection propriedade.

Cada LivePresenceUser instância e LivePresenceConnection tem uma state propriedade, que pode ser online, offlineou away. Um presenceChanged evento é emitido quando o estado de um usuário é alterado. Por exemplo, se um usuário sair de uma reunião, seu estado será alterado para offline.

Observação

Pode levar até 20 segundos para uma LivePresenceUserstate atualização offline após sair de uma reunião.

Exemplo livestate

A captura de tela mostra um exemplo do estado do Live Share para sincronizar qual planeta no sistema solar é apresentado ativamente à reunião.

A LiveState classe permite sincronizar o estado do aplicativo simples para todos em uma reunião. LiveState sincroniza um único state valor, permitindo que você sincronize qualquer valor serializável JSON, como um string, numberou object.

Veja a seguir alguns exemplos em que LiveState podem ser usados em seu aplicativo:

  • Definindo o identificador de usuário do apresentador atual para criar um recurso de controle take .
  • Sincronizando o caminho de rota atual do seu aplicativo para garantir que todos estejam na mesma página. Por exemplo, /whiteboard/:whiteboardId.
  • Mantendo o identificador de conteúdo que o apresentador atual está exibindo. Por exemplo, um taskId em um quadro de tarefas.
  • Sincronizando a etapa atual em uma atividade de grupo de várias rodadas. Por exemplo, a fase de adivinhação durante o jogo do Agile Poker.
  • Manter uma posição de rolagem em sincronização para um recurso siga-me .

Observação

Ao contrário SharedMapde , o state valor em LiveState será redefinido depois que todos os usuários se desconectarem de uma sessão.

Exemplo:

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.
  // To know which user made this change, you can pass the `clientId` to the `getUserForClient()` API from the `LivePresence` class.
});

// 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");

Exemplo liveEvent

A captura de tela mostra um exemplo de cliente do Teams exibindo a notificação quando há uma alteração no evento.

LiveEvent é uma ótima maneira de enviar eventos simples para outros clientes em uma reunião que só são necessárias no momento da entrega. É útil para cenários como enviar notificações de sessão ou implementar reações 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", (kudosReaction, local, clientId) => {
  console.log("Received reaction:", kudosReaction, "from clientId", clientId);
  // To know which user made this change, you can pass the `clientId` to the `getUserForClient()` API from the `LivePresence` class.
  // 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);

Exemplo do LiveTimer

A captura de tela mostra um exemplo de um temporizador de contagem regressiva com 9 segundos restantes.

LiveTimer fornece um temporizador de contagem regressiva simples que é sincronizado para todos em uma reunião. É útil para cenários que têm um limite de tempo, como um temporizador de meditação em grupo ou um temporizador redondo para um jogo.

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();

Verificação de função para estruturas de dados dinâmicas

As reuniões no Teams incluem chamadas, reuniões de todas as mãos e salas de aula online. Os participantes da reunião podem abranger organizações, ter privilégios diferentes ou simplesmente ter objetivos diferentes. Portanto, é importante respeitar os privilégios de diferentes funções de usuário durante as reuniões. Objetos live são projetados para dar suporte à verificação de função, permitindo que você defina as funções que podem enviar mensagens para cada objeto vivo individual. Por exemplo, você pode optar por permitir que apenas os apresentadores e organizadores da reunião controlem a reprodução de vídeo, mas, ainda assim, permitir que convidados e participantes solicitem vídeos para assistir em seguida.

Observação

A LivePresence classe não dá suporte à verificação de função. O LivePresenceUser objeto tem um getRoles método, que retorna as funções de reunião para um determinado usuário.

No exemplo a seguir, em que apenas apresentadores e organizadores podem assumir o controle, LiveState é usado para sincronizar qual usuário é o apresentador ativo:

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

Ouça seus clientes para entender seus cenários antes de implementar a verificação de função em seu aplicativo, principalmente para a função de Organizador. Não há nenhuma garantia de que um organizador esteja presente na reunião. Como regra geral, todos os usuários são Organizador ou Apresentador ao colaborar em uma organização. Se um usuário for um Participante, geralmente é uma decisão intencional do organizador.

Em alguns casos, um usuário pode ter várias funções. Por exemplo, um Organizador também é um Apresentador. Além disso, os participantes da reunião que são externos ao locatário que hospeda a reunião têm a função convidado , mas também podem ter privilégios de Apresentador . Isso fornece muita flexibilidade na forma como você usa a verificação de função em seu aplicativo.

Observação

O SDK do Live Share não tem suporte para usuários convidados em reuniões de canal.

Estruturas de dados distribuídos do Fluid

O SDK do Live Share dá suporte a qualquer estrutura de dados distribuída incluída no Fluid Framework. Esses recursos servem como um conjunto de primitivos que você pode usar para criar cenários colaborativos robustos, como atualizações em tempo real de uma lista de tarefas ou texto de coautoria em um HTML <textarea>.

Ao contrário das classes mencionadas neste artigo, as estruturas de dados fluidas não são redefinidas após o LiveDataObject fechamento do aplicativo. Isso é ideal para cenários como o painel lateral da reunião, em que os usuários frequentemente fecham e reabrem seu aplicativo usando outras guias na reunião, como chat.

O Fluid Framework dá suporte oficialmente aos seguintes tipos de estruturas de dados distribuídas:

Objeto Compartilhado Descrição
SharedMap Um repositório de chave-valor distribuído. Defina qualquer objeto serializável JSON de uma determinada chave para sincronizar esse objeto para todos na sessão.
SharedSegmentSequence Uma estrutura de dados semelhante à lista para armazenar um conjunto de itens (chamados segmentos) em posições definidas.
SharedString Uma sequência de cadeias de caracteres distribuídas otimizada para editar o texto de documentos ou áreas de texto.

Vamos ver como o SharedMap funciona. Neste exemplo, usamos SharedMap para criar um recurso de playlist.

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 as SharedMap;

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

Observação

Os objetos DDS do Fluid Framework principal não dão suporte à verificação de função de reunião. Todos na reunião podem alterar os dados armazenados por meio desses objetos.

Exemplos de código

Nome do exemplo Descrição JavaScript
Dice Roller Habilite todos os clientes conectados para rolar um dado e exibir o resultado. Exibir
Agile Poker Permita que todos os clientes conectados joguem o Agile Poker. Exibir

Próxima etapa

Confira também